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.
Related
As of Identity 2 they have switched from a id with an integer value (ex. 1,2,3,4,...) to a id with a nvarchar value that stores the id as some long string like
a234vt-23sdlj23klj-34jkh34jh34-23jk4jh2
If I'm creating an object that will have a single owner belonging to the person logged in, so I need to attach the user id to it, should I use this new id from Identity 2 or should I try and create some other value like an integer and put it into the aspnetusers table? Does it really matter, all I'm doing is fetching Gift object by owner(userid) and displaying/modifying gift objects on a form.
here is an example of my product object
public class Gift
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Category> Categories { get; set; }
public int Rating { get; set; }
public GiftStatus Status { get; set; }
Public string UserId {get; set; //where userid is the id of the user that owns this object, should it be 3kj23jh3-h3hk1jh2-khj2h34l1b-n22g35l ???
}
There is no problem with using the Guid UserId to reference a user, if that's what you're concerned about. Unless you have a specific reason for not wanting to use a Guid as the UserId, I would suggest you just use the default behavior in order to simplify implementation. Having two separate Ids to keep track of a user sounds needlessly complicated, I wouldn't recommend that path.
If you do have a good reason for requiring an Int as the primary key instead of a Guid, you might take a look at this: http://blogs.msdn.com/b/webdev/archive/2014/03/20/test-announcing-rtm-of-asp-net-identity-2-0-0.aspx (scroll down to the "Make the type of Primary Key be extensible for Users and Roles" section). This page has a link to an example project which shows you how to use an Int as the PK. It also mentions that this extension can be used to migrate applications which use Int PKs to the new Identity 2.0.
Here's another article that may be helpful: http://www.codeproject.com/Articles/777733/ASP-NET-Identity-Change-Primary-Key
I would like to create a group in my app. In model that group has a field Admin_ID to specify who is the owner. How to include that information in a form? With hidden field? I don't think this would be safe enough solution... Any other ideas?
In model that group has a field Admin_ID to specify who is the owner.
Then apply authentication (see the tutorial for your MVC version) so you know which user is logged in, use authorization to restrict access to admins only and in the controller assign the creating user's ID to the model when it is created:
[Authorize(Roles="Admin")]
public ActionResult Create(SomeModel modelToCreate)
{
// get user, depends on how your project is set up...
var user = GetLoggedOnUser();
modelToCreate.Admin_ID = user.ID;
}
If hidden field is not enough for security - don't include it there, but have a separate action that only does operation on your Admin group.
Sorry, can't give more details, as your question does not contain enough information.
You can always save the Admin_ID in the Session.
To better understand:
public class Group
{
public int A { get; set; }
public string B { get; set; }
public string C { get; set; }
public string ID_Admin { get; set; }
}
where ID_Admin should be automatically generated when someone who is logged in want to submit that form (he need to fill only A, B and C fields). So let's say currently logged user has ID 42, and he want to submit that form (without any knowledge of that additional field of course) - and I want to include that information about his ID in final model submission.
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 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.
I am creating a Web application using ASP.NET MVC, and I'm trying to use domain-driven design. I have an architecture question.
I have a WebControl table to store keys and values for lists so they can be editable. I've incorporated this into my business model, but it is resulting in a lot of redundant code and I'm not sure it belongs there. For example, in my Request class I have a property called NeedType. Because this comes from a list, I created a NeedType class to provide the values for the radio buttons. I'm showing just one example here, but the form is going to have probably a dozen or so lists that need to come from the database.
[edit, to clarify question] What's a better way to do this? Are these list objects really part of my domain or do they exist only for the UI? If not part of the domain, then they don't belong in my Core project, so where do they go?
public class Request : DomainObject
{
public virtual int RequestId { get; set; }
public virtual DateTime SubmissionDate { get; set; }
public virtual string NeedType { get; set; }
public virtual string NeedDescription { get; set; }
// etc.
}
public class NeedType : DomainObject
{
public virtual int NeedTypeId { get; set; }
public virtual string NeedTypeCode { get; set; }
public virtual string NeedTypeName { get; set; }
public virtual int DisplayOrder { get; set; }
public virtual bool Active { get; set; }
}
public class RequestController : Controller
{
private readonly IRequestRepository repository;
public RequestController()
{
repository = new RequestRepository(new HybridSessionBuilder());
}
public RequestController(IRequestRepository repository)
{
this.repository = repository;
}
public ViewResult Index(RequestForm form)
{
ViewData.Add("NeedTypes", GetNeedTypes());
if (form == null)
{
form = new RequestForm();
form.BindTo(repository.GetById(125));
}
}
private NeedType[] GetNeedTypes()
{
INeedTypeRepository repo = new NeedTypeRepository(new HybridSessionBuilder());
return repo.GetAll();
}
}
Create a seperate viewmodel with the data you need in your view. The Model in the M of MVC is not the same as the domainmodel. MVC viewmodels are dumb DTO's without behaviour, properties only. A domain model has as much behaviour as possible. A domain model with get;set; properties only is considered an anti-pattern called "anemic domain model". There are 2 places where most people put the viewmodels: in the web layer, close to the views and controllers, or in a application service layer.
Edit:
When you only need to display a list of all needtypes in the database and one request in your view, I would indeed create one viewmodel with the request and the list of needtypes as properties. I don't think a call to multiple repositories in a controller is a smell, unless you have a larger application and you might want a seperate application service layer that returns the whole viewmodel with one method call.
I think it might also be a good idea to follow the advise of Todd Smith about value object.
When the needtypes can be added or edited by users at runtime, needtype should be an entity. When the needtypes are hardcoded and only changed with new releases of the project, needtype should be a value object and the list of needtypes could be populated by something like NeedType.GetAll() and stored in the database by adding a column to the request table instead of a seperate needtype table.
If it comes from a list, then I'm betting this is a foreign key. Don't think about your UI at all when designing your domain model. This is simply a case where NeedType is a foreign key. Replace the string NeedType with a reference to an actual NeedType object. In your database, this would be a reference to an id.
When you're building your list of NeedType choices, you simply need to pull every NeedType. Perhaps keeping it cached would be a good idea if it doesn't change much.
Your NeedType looks like a value object to me. If it's read-only data then it should be treated as a value object in a DDD architecture and are part of your domain.
A lot of people run into the "omg so much redundancy" issue when dealing with DDD since you're no longer using the old Database -> DataTable -> UI approach.