The reason I need a role-based system:
Restrict access to pages.
Restrict access to certain features on pages.
Check/validation inside service layer.
So I'm guessing, I can just create an enum and if I need a new role, just add it to the app code (the app would change anyways so requires a recompile).
So right now I have
public class User
{
/* .. */
public virtual ICollection<UserRole> Roles {get; set;}
}
public enum UserRoleType
{
Administrator,
User
}
public class UserRole
{
public int UserRoleId { get; set; }
public int RoleTypeValue { get; set; }
public UserRoleType RoleType
{
get { return (UserRoleType)RoleTypeValue; }
set { RoleTypeValue = (int)value; }
}
public virtual User User { get; set; }
}
This is a 1 to many. The pros I see for this is that instead of a many-many, there is a 1-many and joins are less. The application already knows what the role is based on what the int resolves the enum to.
Are there any flaws in the way Im doing this? Is there anything you have met in your experience that would require me to store the actual values in the database?
To be clear, you are suggesting that you don't need an actual lookup table in the database for Roles? Instead, they just have an int that is not a foreign key to anything--it is simply a representation of the enum in the application?
It's impossible to answer definitively without knowing all the details of your application. That disclaimer aside, I see nothing inherently problematic about it. It would certainly work fine for some systems.
Possible downsides:
There is no source of valid values enforced on the database side via referential integrity. What is to stop someone from entering "12452" for the RoleId in the database column? There are other ways around this like using check constraints, but they are not necessarily easier to maintain than a lookup table.
There is no way to effectively query the user/roles tables and have a human-readable representation of roles without knowing what the RoleIds represent (you will have to have the enum in front of you to make sense of the query result).
If the database is used for other applications, the roles will need to be represented (and maintained) in that application as well.
Related
I'm fairly new to setting up security for websites and am having trouble finding the correct architecture/design/pattern/best practice for the type of authentication/authorization I am needing in a .NET MVC environment. I don't even know what to call it in order to do more research. Below is an example of what I need to implement. What is this called? (I don't think it's multi-tennant.)
Joe works inventory for a few stores in a Grocery Store chain. Joe is an Inventory Manager(can edit items) for Store A, but just an Inventory Clerk(only view items) for Store B and has no access to Store C.
So Joe should be able to access the ActionResult Edit in the InventoryController if he is trying to edit Store A, but should not be able to access the same ActionResult Edit if he is trying to edit Store B or C.
The straight-forward Identity or Claims based authorization isn't enough for this scenario (I don't think), but I don't know the "name" of the design I need in order to do further research. What is this design called?
It's called object-level authorization (aka object-level security, aka fine-grained authorization, etc.). Basically, permissions are based on "ownership" of objects, or perhaps better put in this scenario, being owned by an object. You would need to set up a many-to-many relationship between stores and employees, with payload of a role/grant. For example:
public class StoreEmployee
{
[Key, Column(Order = 1)]
[ForeignKey("Store")]
public int StoreId { get; set; }
public virtual Store Store { get; set; }
[Key, Column(Order = 2)]
[ForeignKey("Employee")]
public int EmployeeId { get; set; }
public virtual Employee Employee { get; set; }
public string Role { get; set; }
}
public class Store
{
...
public virtual ICollection<StoreEmployee> Employees { get; set; }
}
public class Employee
{
...
public virtual ICollection<StoreEmployee> Stores { get; set; }
}
With that, then you can use this relationship in your actions to verify whether a user has access:
if (!joe.Stores.Any(m => m.Store == storeA && m.Role == "Manager"))
{
return new HttpUnauthorizedResult();
}
Here, I kept things simple by just making Role a string. You could use a enum, or even an actual class that would also be persisted in your database. Or you could tie into the existing roles for users in general. It's up to you. You might also prefer to turn that into a custom action filter.
You could set this up as a multi tenant system. If every store is a tenant with its own user directory, then Joe would need to login to a different directory for store A then for store B and would get a another role assigned.
Joe would not be able to login to store C as he does not have an account in that directory.
If you want users to authenticate through a federated system, you'd need to set up a role per store and assign those based on which IdP the user came from.
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
I am trying to create a permissions system in an ASP.NET MVC Application. I have been learning the newest Identity framework - here are my requirements:
A set of Hierarchical Roles for each set of functionality. So, for instance, there might be the following roles:
Inherit
Reader
Editor
Manager
Administrator
Each user would have one of those roles for each module (e.g. Events, Pages, etc.)
Users can be members of a security group. Each security group can be assigned a role directly and then all users in that group (who have not been explicitly assigned that permission) will inherit that role.
Multi-tenant site: each user has a set of sites which they are a member of. In the context of each site, they have a complete set of permissions which can be assigned by the site admin.
Through extending ASP.NET Identity, is it going to be possible for me to accomplish all of this? Or should I be building something custom from the ground-up?
9,999 times out of 10,000 implementing your own authentication system is the wrong way to go. Anything is easier than that, and it's a deceptively difficult thing to do right. ASP.NET Identity is actually pretty customizable, as it was created specifically for that purpose. You might need to do quite a bit to bootstrap your custom requirements fully, but it'll almost certainly be quicker and more secure using ASP.NET Identity.
UPDATE
UserManager's constructor takes an implementation of IUserStore. When working with Entity Framework, you typically just feed it Microsoft.AspNet.Identity.EntityFramework.UserStore, but this is your tie in point for extensibility. So, you can simply subclass UserStore and then override something like GetRolesAsync to do whatever custom role logic you need to implement. Then you'd just feed UserManager your subclass.
In version 1.0 of ASP.NET Membership, the IRole interface must have a string primary key. However in version 2.0, released March 2014, they added an IRole<TKey> that allows you to specify a role's primary key, as long as TKey implements IEquatable<TKey>.
That said, out of the box MVC integration points for authorization still depend on roles being ID'd by a single string. So if you are going to do authorization there, via attributes like Authorize, then you may need to write your own custom authorization attributes.
One way to achieve hierarchical roles would be to handle it in your application instead of in the model. I assume by hierarchical, you mean that Administrators have all the privileges of Managers, Managers have all the same privileges as Editors, and so on. You could achieve this by adding users to multiple roles, instead of having to walk through a modeled role hierarchy. Something like a db trigger could do it, but you can model it as a business rule in code too. Then if you restrict a page to Editor, Admins & Mgrs would have access to it as well.
Another way would be to just authorize certain actions for multiple roles:
[Authorize(Roles = "Administrator, Manager, Editor")]
public ActionResult Edit()
[Authorize(Roles = "Administrator, Manager")]
public ActionResult Manage()
[Authorize(Roles = "Administrator")]
public ActionResult Admin()
I disagree though that you would want to id roles on a composite key. If you want to protect MVC actions using the Authorize attribute, the ID of the role needs to be a constant value, like a string literal, int or Enum value, etc. If you keyed role on more than one of its properties, the number of properties you need to set on the attribute multiplies by the number of values in each component of the id. You would have Manager/SiteA, Manager/SiteB, and so on.
Instead, it sounds like it might be a good idea to just add properties to the gerund that tracks users in roles (the in-between table in a many-to-many relationship). To do this, you wouldn't be able to simply override and extend methods in the UserManager class as #Chris Pratt suggested. But that doesn't mean you have to throw out the baby with the bathwater. You can still use Microsoft.AspNet.Identity for authentication, and just write your own methods for role management, augmenting them to take an additional parameter:
AddToRoleAsync(TUser user, string roleName, string siteId);
public class Role : IRole<int>
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<UserInRole> Authorizes { get; set; }
}
public class UserInRole
{
public int RoleId { get; set; } // part of composite primary key
public int UserId { get; set; } // part of composite primary key
public string SiteId { get; set; } // part of composite primary key
public virtual Role Role { get; set; }
public virtual User User { get; set; }
}
public class User : IUser<int>
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<UserInRole> Authorized { get; set; }
}
Given the above, say your URL's look something like this:
/sites/site-a/admin
/sites/site-b/manage
/sites/site-c/edit
/sites/{siteId}/do
...you could write a custom authorization attribute that checks the URL and authorizes the principal both against the role name in the attribute and the siteId in the URL. To get access to the db from the attribute, if you are using IoC for EntityFramework, you can property inject an instance of your DbContext (or whatever interface you have wrapping it).
Following steps can solve your problem
Create granular level of roles...typically for each action
Group them up into GroupRoles...so that admin can easily manage it
Add individual level claims to user for specific permission
Some good examples of the same are below
http://www.3pillarglobal.com/insights/granular-level-user-and-role-management-using-asp-net-identity
http://bitoftech.net/2015/03/11/asp-net-identity-2-1-roles-based-authorization-authentication-asp-net-web-api/
Hope this solves your problem
I am using Code First to automatically generate my database, and this works perfectly, generating an Orders table and an OrderLines table as expected when I add some test data.
I have the following Order class:
public class Order
{
public int OrderID { get; set; }
public void AddItem(string productCode, int quantity)
{
var existingLine = OrderLines.FirstOrDefault(x => x.ProductOption.ProductCode == item.ProductCode);
if (existingLine == null)
OrderLines.Add(new OrderLine { ProductOption = item, Quantity = quantity });
else
existingLine.Quantity += quantity;
}
public void RemoveItem(string productCode)
{
OrderLines.Remove(OrderLines.Where(x => x.ProductOption.ProductCode == productCode).FirstOrDefault());
}
public virtual ICollection<OrderLine> OrderLines { get; set; }
public Order()
{
OrderLines = new List<OrderLine>();
}
}
What I really want is to encapsulate the OrderLines collection, making it impossible for consumers of the class to directly add and remove items to/from it (using the Add / Remove methods of ICollection) and instead forcing them to use my custom AddItem and RemoveItem methods.
Normally I could just make the collection private, but I can't do that because it needs to be virtual for EF to correctly create the OrderLines table/foreign keys.
This answer seems to suggest that making the property internal would do the trick, but I tried, and in that case no OrderLines table is created.
Is there any way that this can be accomplished, or should I have designed this differently somehow? Any help much appreciated!
Update
After a bit more searching, I found this question which is rather more clearly stated than mine; however, it's still unanswered. The poster does link to this post which seems to suggest it can't really be done in the way I'm thinking of, but does anyone have any more up-to-date information?
I don't know if it's possible to do what you are asking or not, but I'm not sure it's the best design. The problem that I am seeing is you are firmly integrating your business logic into your business entities, and I think this will turn into confusion down the road.
Take the following scenario under consideration. Say you have a new requirement where you want users to be able to remove all items from an order. The only way to do it with your entity is to create a new RemoveAllItems() method to your Order class which does that. Now say you have a new requirement to Remove all items from an order that are in a specific category. That then means that you have to add yet another method.
This causes really bloated classes, and there is one major issue you will come up with. If you (or another developer) want to look at an entity and determine it's data structure, you can't at a glance because it's so intertwined with business logic.
What I would suggest is that you keep your entities as pure data structures, keeping all their relationships public. Then you need to create a service layer, which can consist of small or big classes (however you want to organize them) that actually perform the business functions. So for example, you can have a OrderItemService class, which has methods for adding, editing, and removing items from an order. All your business logic is performed in this class, and you just have to enforce that only service classes are allowed to interact with db entities.
Now, if you are looking for how a particular business process is performed, you know to look in the service layer classes, and if you want to look at how a data structure or entity is organized, you look at the entity. This keeps everything clean and very mantainable.
I'm far from an expert on code first and I haven't tried the following but is it possible to use the ReadOnlyCollectionBase and create a read only list similar to this MSDN article?
Well what you can do is set your collection as private and make the relationship using fluent API in the OnModelCreating, as shown below, I don't know if this will work, just make a try:
public class YourContext : DbContext
{
public DbSet<Order> Orders { get; set; }
public DbSet<OrderLine> OrderLines { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Order>()
.HasMany(o => o.OrderLines)
.WithRequired(l => l.OrderId)
.HasForeignKey(l => l.OrderId);
}
}
This will make your OrderLines as readonly:
public class YourContext : DbContext
{
public DbSet<Order> Orders { get; set; }
public DbSet<OrderLine> OrderLines
{
get { return set<OrderLine>(); }
}
}
I hope this can help you, please take a look a this blog post: EF Feature CTP5: Fluent API Samples
I am in the planning phase of a new ASP.NET MVC application and one of the requirements is storing some user information that is not part of the standard set found in the User class that comes with ASP.NET MVC. I suppose it comes down to two questions.
1) Can I edit the class that is being used already to store the information that I need?
2) If I roll my own how can I keep things like the Authentication piece that make things so nice when trying to lock down some views using the User.IsAuthenticated method?
Another alternative I have considered is using the User class provided as is, and instead putting the other information into a separate table with the guid userid as the foreign key.
Suggestions?
Profiles are one option as #Burt says, and offers a lot of flexibility.
I had a similar need to track Employee information, but I opted to roll my own Employee class and create a relationship to a standard User. I really like how this has worked out as I can keep any Employee specific business logic separate from the User class Membership system.
Since not every User was going to be bound with an employee, this made more sense for my case. It may not for yours, but it is an alternative.
So, I have something like:
public class Employee
{
public Employee(string name) : this()
{
Name = name;
}
public virtual string Name { get; set; }
public virtual string Title { get; set; }
public virtual decimal Salary { get; set; }
public virtual decimal Hourly { get; set; }
public virtual decimal PerDiem { get; set; }
public virtual string StreetAddress { get; set; }
public virtual Guid UserId { get; set; }
public virtual MembershipUser User {
get
{
// note that I don't have a test for null in here,
// but should in a real case.
return Membership.GetUser(UserId);
}
}
}
See ASP.Net MVC Membership Starter Kit. It provides the Asp.Net MVC controllers, models, and views needed to administer users & roles. It will cut distance in half for you.
Out of the box, the starter kit gives you the following features:
List of Users
List of Roles
User
Account Info
Change Email Address
Change a User's Roles
Look into profiles that are part of the membership functionality provided by MS. They are extendable and pretty flexible.
For a given report, the user will want to have multiple filtering options. This isn't bad when the options are enumerations, and other 'static' data types, however things can get silly fast when you need a select list that is populated by fields stored in a table in the backend.
How do you handle this scenario? I find myself constantly reshaping the View data to accommodate the additional filter fields, but it really is starting to be a bit much tracking not only the selected options, but also the options themselves...
is there not a better way?
I’m currently building out a new reporting section for one of our products at work and am dealing with this same issue. The solution I’ve come up with so far, though it hasn’t been implemented yet so this is still a work in progress, is along the lines of this.
There will be a class that will represent a report filter which will contain some basic info such as the label text and a list of option values.
public enum DisplayStyle
{
DropDown,
ListBox,
RadioList,
CheckList,
TextBox
}
public class FilterOption
{
public string Name { get; set; }
public string Value { get; set; }
public bool Selected { get; set; }
}
public class ReportFilter
{
public string Title { get; set; }
public DisplayStyle Style { get; set; }
public List<FilterOption> Options { get; set; }
}
And then my model will contain a list of these option classes that will be generated based on each report’s needs. I also have a base report class that each report will inherit from so that way I can handle building out the option lists on a per report basis and use one view to handle them all.
public class ReportModel
{
public string Name { get; set; }
public List<ReportFilter> Filters { get; set; }
public DateTime StartDate { get; set; }
public DateTime EndDate { get; set; }
}
Then inside my view(s) I’ll have some helper methods that will take in those option classes and build out the actual controls for me.
public static string ReportFilter(this HtmlHelper htmlHelper, DisplayStyle displayStyle, FilterOption filterOption)
{
switch (displayStyle)
{
case DisplayStyle.TextBox:
return string.Format("<input type=\"text\"{0}>", filterOption.Selected ? (" value=\"" + filterOption.Value + "\"") : string.Empty);
break;
...
}
}
My route would look like this
Reports/{reportID}/start/{startDate}/end/{endDate}/{*pathInfo}
All reports have a start and end date and then optional filters. The catchall parameter will have lists of filter values in the form of “Customer/1,4,7/Program/45,783”. So it’ll be like a key/value pair in list form. Then when the controller loads it’ll parse out those values into something more meaningful.
public static Dictionary<string, string> RouteParams(string pathInfo)
{
if (string.IsNullOrEmpty(pathInfo))
{
return new Dictionary<string, string>();
}
var values = new Dictionary<string, string>();
// split out params and add to the dictionary object
return values;
}
Then it will pass them off to the report class and validate them to make sure they’re correct for that report. Then when the options are loaded for that report anything that’s been set in the URL will be set to Selected in the ReportOption class so their state can be maintained. Then the filter list and other report data will be added to the model.
For my setup some filters will change when another filters selection changes so there will be some AJAX in here to post the data and get the updated filter options. The drilldown will work sort of like the search options at amazon or newegg when you narrow your search criteria.
I hope that all makes sense to someone beside me. And if anyone has some input on improving it I’d be happy to hear it.
You could go and retrieve the data asynchronously on the screen using jQuery and JsonResults from your MVC application, this is how we populate all of our lists and searches in our applications. I have an example of how it is done here.
This way the view data is loaded on demand, if they don't use the extra filters then they don't have to get the view data and if one selection relates to another then it's clear which set of data you need to retrieve.
Another option, though I don't like this one as much but jQuery solution may not suit you, is to have your model object for your view contain all the view data so that all you need to do is set the single model object and all the lists are loaded directly and strongly typed. This will simplify the view and the back end code because it will be more clear that for this view the only thing you need is a complete version of this model object.
For example if you had two lists for combo boxes then your model might look like:
public class MyViewMode
{
public int MyProperty { get; set; }
public string SomeString { get; set; }
List<string> ComboListA { get; set; }
List<string> ComboListB { get; set; }
}
Hope that makes sense, if not please comment and I'll expand on it.
Ad-hoc filtering on reports is indeed a tricky issue especially when you want to show a custom user interface control based on the data type, do validation, make some filters to be dependent on one another and others not, etc.
One thing I think that is worth considering is the old "build vs buy" issue here. There are specialized tools out there for ad-hoc reporting that do provide a UI for ad-hoc filters help with this such as the usual suspects Crystal Reports, Microsoft's Reporting Services, or our product ActiveReports Server. In ActiveReports Server we support cascading prompts (where available values in prompts depend on one another) and make it easy for anyone, even non-technical business users to modify the prompts (assuming they have permissions obviously). More information about using prompts in ActiveReports Server is here. ActiveReports Server is also, all managed .NET code, and provides ASP.NET controls and web services that allows you to integrate it into your web apps.
Scott Willeke
Product Manager - ActiveReports Server
GrapeCity inc.