MVC5 EF6 UserRights - asp.net-mvc

I wonder what is the best practise for checking actually logged user's rightsfor in MVC appliaction. I would like to do this on the views and on the controllers.
Actually I can check if actual logged user is in the role like that:
User.IsInRole("roleName");
I have table with rights. Every right contain name and Enum right representation. What I want to accomplish is extension method for IPrincipal or IIdentity so I could check rights of the user like that:
Example extension method:
public static class MyPrincipal
{
public static string HasRight(this IPrincipal principal, EnumRight right)
{
// THIS would be place where I would check if a user have right with specific enum.
return true/false;
}
}
and then on the view or controller I could use this like that:
if(User.HasRight(EnumRight.AddDocuments) )
{
//DO SOMETHING
}
Is it good practise or how can I do similar mechanism simpler? Maybe independent static class not extension?
How can i get actual user Id in my extension method to get from database any data which is needed to check if user has rights?

Normally, you would implement an authorization filter, and apply it to your action method. Here is the link how you can implement action filters.

Related

Good approach for globally accessing the Logged-in User

I want to be able to globally access the logged-in user in (Controllers, HtmlHelpers and other helper classes) throughout the whole application without getting exceptions that User is null and with the ability to handle guests (not-logged-in users).
Should this be handled within my UsersRepository? If so, how to? or is there a better approach?
You can create custom identity and principal classes. Your custom identity class can extend whatever you are currently using and can then simply add the extra information you need.
Then in a global action filter, simply overwrite the current principal with your customized one. Then you can access it from anywhere like a normal identity, but if you need your additional information, you simply cast it to your custom identity class. Which will grant you access to your additional information.
You can write a custom action filter that is executed on every request (you register it as a global filter). This filter would load the user (from the user´s repository for example) and put it the http context for example or in the ViewData.
EDIT:
Ok, the code for the filter could look like this (in this case, it loads the user to the ViewData collection). I didn´t consider anonymous users here.
public class LoadUserToViewDataAttribute : ActionFilterAttribute
{
private IUserRepository _userRepository;
public LoadUserToViewDataAttribute(IUserRepository userRepository)
{
_userRepository = userRepository;
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var controller = filterContext.Controller;
var userName = filterContext.HttpContext.User.Identity.Name;
var user = _repository.GetUser(userName);
controller.ViewData.Add("CurrentUser", user);
}
}

Enforce security check in ASP.NET MVC?

I am implementing a collaborative web gallery, and I have a few roles for each user:
Admin
DeleteImage
DeleteOwnImage
etc..
For any controller-action, we can apply [Authorize] tag to them plus which roles we want to allow, right? It is fine for Admin/DeleteImage since these two are global; but my question is, like DeleteOwnImage is kind of contextual, in order to determine whether it is valid, we need:
To know what image it is trying to delete (from request)
Retrieve the owner of that image (from service or repository)
Compare current user = that owner
Obviously [Authorize] is not enough to do so, but is it possible to do that on custom ActionFilters? Any hint?
Yes, this is possible with a custom action filter. You can extend from AuthorizeAttribute, the most basic implementation being something like:
public class OwnImageAuthorizeAttribute : AuthorizeAttribute {
public string ImageIdKey { get; set; }
protected override bool AuthorizeCore(HttpContextBase httpContext) {
bool authorized = false;
// Get the current user
var currentUser = ...;
// Get the image ID, whether it is in the route or querystring
int imageId
if(int.TryParse(httpContext.RouteData.Values(ImageIdKey), out imageId)) {
// From querystring: httpContext.Request.Querystring[ImageIdKey]
// Authorize the user
authorized = YourMethodToCheckIfUserIsOwner(currentUser, imageId);
}
return authorized;
}
Then, decorate your method:
[OwnImageAuthorize(ImageIdKey = "imageId")]
public ActionResult MyAction() { }
You can find some more details here.
You can easily add something like this in an ActionFilter, Just add an action filter with OnActionExecuting implemented.
Although, depending on your DB schema this could be achieved on the DB level with your query. You could just delete when owner equals recieved recieved id. (I mean, inside the action method and not in the filter)
EDIT:
If you're using some kind of IOC container for the repositories, you should look around for the new IOC features in MVC3 (if you're using MVC3) to inject dependencies into your action filters.
http://bradwilson.typepad.com/blog/2010/07/service-location-pt4-filters.html
EDIT2:
BTW, I myself don't really like doing too much business logic in ActionFilters, especially involving calls to the DB. Even more when it's something very specific that'll be used for one action.

How can I Implement a Custom RoleProvider?

I'm trying to implement a custom RoleProvider in my ASP.NET MVC application.
I've created a custom MembershipProvider and it works, in that I'm able to successfully validate the user. The next step is to implement the RoleProvider to restrict access to certian Controllers to Admin users only.
Can anyone provide me with a quick outline of the steps I need to take?
The point that I'm at now is I have my controller with the Authorize filter, like so:
[Authorize(Roles="Admin")]
public class AdminOnlyController : Controller
{
// stuff
}
and I have my CustomRoleProvider class, with the following method along with a load of not-implemented Methods:
public override string[] GetRolesForUser(string username)
{
if (username == "dave")
{
return new string[] { "Admin" };
}
}
I think I need to add the user to the Role somehow but I don't know how to do that. Ideally the end result would be a scenario where unauthorized users can't access certain controllers, and I in my Views I could determine whether to show links with something like:
if (User.IsInRole("Admin"))
{
// show links to Admin Controllers
}
Can anyone point me in the right direction?
I used this as as base line for a custom role manager: http://davidhayden.com/blog/dave/archive/2007/10/17/CreateCustomRoleProviderASPNETRolePermissionsSecurity.aspx
Should work in MVC or Web Forms.
UPDATE: Since that web page no longer exists, you could try this one instead. The basic requirements are that you need to implement the RoleProvider interface, namely:
void AddUsersToRoles(string[] usernames, string[] roleNames)
string[] GetRolesForUser(string id)
bool RoleExists(string roleName)
For the not-implemented methods, be sure to throw a NotImplementedException. This should help you figure out which methods are needed in your custom provider to get the job done.
I suspect you'll have to implement IsUserInRole.

Access Control in ASP.NET MVC depending on input parameters / service layer?

Preamble: this is a bit of a philosophical question. I'm looking more for the "right" way to do this rather than "a" way to do this.
Let's imagine I have some products, and an ASP.NET MVC application performing CRUD on those products:-
mysite.example/products/1
mysite.example/products/1/edit
I'm using the repository pattern, so it doesn't matter where these products come from:-
public interface IProductRepository
{
IEnumberable<Product> GetProducts();
....
}
Also my Repository describes a list of Users, and which products they are managers for (many-many between Users and Products). Elsewhere in the application, a Super-Admin is performing CRUD on Users and managing the relationship between Users and the Products they are permitted to manage.
Anyone is allowed to view any product, but only users who are designated as "admins" for a particular product are allowed to invoke e.g. the Edit action.
How should I go about implementing that in ASP.NET MVC? Unless I've missed something, I can't use the built-in ASP.NET Authorize attribute as first I'd need a different role for every product, and second I won't know which role to check for until I've retrieved my Product from the Repository.
Obviously you can generalise from this scenario to most content-management scenarios - e.g. Users are only allowed to edit their own Forum Posts. StackOverflow users are only allowed to edit their own questions - unless they've got 2000 or more rep...
The simplest solution, as an example, would be something like:-
public class ProductsController
{
public ActionResult Edit(int id)
{
Product p = ProductRepository.GetProductById(id);
User u = UserService.GetUser(); // Gets the currently logged in user
if (ProductAdminService.UserIsAdminForProduct(u, p))
{
return View(p);
}
else
{
return RedirectToAction("AccessDenied");
}
}
}
My issues:
Some of this code will need to be repeated - imagine there are several operations (Update, Delete, SetStock, Order, CreateOffer) depending on the User-Products relationship. You'd have to copy-paste several times.
It's not very testable - you've got to mock up by my count four objects for every test.
It doesn't really seem like the controller's "job" to be checking whether the user is allowed to perform the action. I'd much rather a more pluggable (e.g. AOP via attributes) solution. However, would that necessarily mean you'd have to SELECT the product twice (once in the AuthorizationFilter, and again in the Controller)?
Would it be better to return a 403 if the user isn't allowed to make this request? If so, how would I go about doing that?
I'll probably keep this updated as I get ideas myself, but I'm very eager to hear yours!
Thanks in advance!
Edit
Just to add a bit of detail here. The issue I'm having is that I want the business rule "Only users with permission may edit products" to be contained in one and only one place. I feel that the same code which determines whether a user can GET or POST to the Edit action should also be responsible for determining whether to render the "Edit" link on the Index or Details views. Maybe that's not possible/not feasible, but I feel like it should be...
Edit 2
Starting a bounty on this one. I've received some good and helpful answers, but nothing that I feel comfortable "accepting". Bear in mind that I'm looking for a nice clean method to keep the business logic that determines whether or not the "Edit" link on the index view will be displayed in the same place that determines whether or not a request to Products/Edit/1 is authorised or not. I'd like to keep the pollution in my action method to an absolute minimum. Ideally, I'm looking for an attribute-based solution, but I accept that may be impossible.
First of all, I think you already half-way figured it, becuase you stated that
as first I'd need a different role for every product, and second I won't know which role to check for until I've retrieved my Product from the Repository
I've seen so many attempts at making role-based security do something it was never intended to do, but you are already past that point, so that's cool :)
The alternative to role-based security is ACL-based security, and I think that is what you need here.
You will still need to retrieve the ACL for a product and then check if the user has the right permission for the product. This is so context-sensitive and interaction-heavy that I think that a purely declarative approach is both too inflexible and too implicit (i.e. you may not realize how many database reads are involved in adding a single attribute to some code).
I think scenarios like this are best modeled by a class that encapsulates the ACL logic, allowing you to either Query for decision or making an Assertion based on the current context - something like this:
var p = this.ProductRepository.GetProductById(id);
var user = this.GetUser();
var permission = new ProductEditPermission(p);
If you just want to know whether the user can edit the product, you can issue a Query:
bool canEdit = permission.IsGrantedTo(user);
If you just want to ensure that the user has rights to continue, you can issue an Assertion:
permission.Demand(user);
This should then throw an exception if the permission is not granted.
This all assumes that the Product class (the variable p) has an associated ACL, like this:
public class Product
{
public IEnumerable<ProductAccessRule> AccessRules { get; }
// other members...
}
You might want to take a look at System.Security.AccessControl.FileSystemSecurity for inspiration about modeling ACLs.
If the current user is the same as Thread.CurrentPrincipal (which is the case in ASP.NET MVC, IIRC), you can simplyfy the above permission methods to:
bool canEdit = permission.IsGranted();
or
permission.Demand();
because the user would be implicit. You can take a look at System.Security.Permissions.PrincipalPermission for inspiration.
From what you are describing it sounds like you need some form of user access control rather than role based permissions. If this is the case then it needs to be implemented throughout your business logic. Your scenario sounds like you can implement it in your service layer.
Basically you have to implement all functions in your ProductRepository from the perspective of the current user and the products are tagged with permissions for that user.
It sounds more difficult than it actually is. First off you need a user token interface that contains the user information of uid and role list (if you want to use roles). You can use IPrincipal or create your own along the lines of
public interface IUserToken {
public int Uid { get; }
public bool IsInRole(string role);
}
Then in your controller you parse the user token into your Repository constructor.
IProductRepository ProductRepository = new ProductRepository(User); //using IPrincipal
If you're using FormsAuthentication and a custom IUserToken then you can create a Wrapper around the IPrincipal so your ProductRepository is created like:
IProductRepository ProductRepository = new ProductRepository(new IUserTokenWrapper(User));
Now all your IProductRepository functions should access the user token to check permissions. For example:
public Product GetProductById(productId) {
Product product = InternalGetProductById(UserToken.uid, productId);
if (product == null) {
throw new NotAuthorizedException();
}
product.CanEdit = (
UserToken.IsInRole("admin") || //user is administrator
UserToken.Uid == product.CreatedByID || //user is creator
HasUserPermissionToEdit(UserToken.Uid, productId) //other custom permissions
);
}
If you wondering about getting a list of all products, in your data access code you can query based on permission. In your case a left join to see if the many-to-many table contains the UserToken.Uid and the productId. If the right side of the join is present you know the user has permission to that product and then you can set your Product.CanEdit boolean.
Using this method you can then use the following, if you like, in your View (where Model is your Product).
<% if(Model.CanEdit) { %>
Edit
<% } %>
or in your controller
public ActionResult Get(int id) {
Product p = ProductRepository.GetProductById(id);
if (p.CanEdit) {
return View("EditProduct");
}
else {
return View("Product");
}
}
The benefit to this method is that the security is built in to your service layer (ProductRepository) so it is not handled by your controllers and cannot be bypassed by your controllers.
The main point is that the security is placed in your business logic and not in your controller.
The copy paste solutions really become tedious after a while, and is really annoying to maintain. I would probably go with a custom attribute doing what you need. You can use the excellent .NET Reflector to see how the AuthorizeAttribute is implemented and perform your own logic to it.
What it does is inheriting FilterAttribute and implementing IAuthorizationFilter. I can't test this at the moment, but something like this should work.
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = true)]
public class ProductAuthorizeAttribute : FilterAttribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationContext filterContext)
{
if (filterContext == null)
{
throw new ArgumentNullException("filterContext");
}
object productId;
if (!filterContext.RouteData.Values.TryGetValue("productId", out productId))
{
filterContext.Result = new HttpUnauthorizedResult();
return;
}
// Fetch product and check for accessrights
if (user.IsAuthorizedFor(productId))
{
HttpCachePolicyBase cache = filterContext.HttpContext.Response.Cache;
cache.SetProxyMaxAge(new TimeSpan(0L));
cache.AddValidationCallback(new HttpCacheValidateHandler(this.Validate), null);
}
else
filterContext.Result = new HttpUnauthorizedResult();
}
private void Validate(HttpContext context, object data, ref HttpValidationStatus validationStatus)
{
// The original attribute performs some validation in here as well, not sure it is needed though
validationStatus = HttpValidationStatus.Valid;
}
}
You could probably also store the product/user that you fetch in the filterContext.Controller.TempData so you can fetch it in the controller, or store it in some cache.
Edit: I just noticed the part about the edit link. The best way I can think of is factoring out the authorization part from the attribute and make a HttpHelper for it that you can use in your view.
I tend to think that authorization is part of your business logic (or at least outside of your controller logic anyway). I agree with kevingessner above, in that the authorization check should be part of the call to fetch the item. In his OnException method, you could show the login page (or whatever you have configured in the web.config) by something like this:
if (...)
{
Response.StatusCode = 401;
Response.StatusDescription = "Unauthorized";
HttpContext.Response.End();
}
And instead of making UserRepository.GetUserSomehowFromTheRequest() calls in all the action methods, I would do this once (in an override of the Controller.OnAuthorization method for example), then stick that data somewhere in your controller base class for later use (e.g. a property).
I think that it's unrealistic, and a violation of the separation of concerns, to expect to have controller/model code control what the view renders. The controller/model code can set a flag, in the view model, that the view can use to determine what it should do, but I don't think that you should expect a single method to be used by both controller/model and view to control both access to and rendering of the model.
Having said that you could approach this in either of two ways -- both would involve a view model that carries some annotations used by the view in addition to the actual model. In the first case, you can use an attribute to control access to the action. This would be my preference, but would involve decorating each method independently -- unless all of the actions in a controller have the same access attributes.
I've developed a "role or owner" attribute for just this purpose. It verifies that the user is in a particular role or is the owner of the data being produced by the method. Ownership, in my case, is controlled by the presence of a foreign key relationship between the user and the data in question -- that is, you have a ProductOwner table and there needs to be a row containing the product/owner pair for the product and current user. It differs from the normal AuthorizeAttribute in that when the ownership or role check fails, the user is directed to an error page, not the login page. In this case, each method would need to set a flag in the view model that indicates that the model can be edited.
Alternatively, you could implement similar code in the ActionExecuting/ActionExecuted methods of the controller (or a base controller so that it applies consistently across all controllers). In this case, you would need to write some code to detect what kind of action is being executed so you know whether to abort the action based on the ownership of the product in question. The same method would set the flag to indicate that the model can be edited. In this case, you'd probably need a model hierarchy so you could cast the model as an editable model so that you can set the property regardless of the specific model type.
This option seems more coupled to me than using the attribute and arguably more complicated. In the case of the attribute you can design it so that it takes the various table and property names as attributes to the attribute and uses reflection to get the proper data from your repository based on the attribute's properties.
Answering my own question (eep!), Chapter 1 of Professional ASP.NET MVC 1.0 (the NerdDinner tutorial) recommends a similar solution to mine above:
public ActionResult Edit(int id)
{
Dinner dinner = dinnerRepositor.GetDinner(id);
if(!dinner.IsHostedBy(User.Identity.Name))
return View("InvalidOwner");
return View(new DinnerFormViewModel(dinner));
}
Asides from making me hungry for my dinner, this doesn't really add anything as the tutorial goes on to repeat the code implementing the business rule immediately in the matching POST Action Method, and in the Details view (actually in a child partial of the Details view)
Does that violate SRP? If the business rule changed (so that e.g. anyone who had RSVP'd could edit the dinner), you'd have to change both GET and POST methods, and the View (and the GET and POST methods and View for the Delete operation too, although that's technically a seperate business rule).
Is pulling the logic out into some kind of permissions arbitrator object (as I've done above) as good as it gets?
You're on the right track, but you can encapsulate all of the permission check into a single method like GetProductForUser, which takes a product, user, and the required permission. By throwing an exception that's caught in the controller's OnException handler, the handling is all in one place:
enum Permission
{
Forbidden = 0,
Access = 1,
Admin = 2
}
public class ProductForbiddenException : Exception
{ }
public class ProductsController
{
public Product GetProductForUser(int id, User u, Permission perm)
{
Product p = ProductRepository.GetProductById(id);
if (ProductPermissionService.UserPermission(u, p) < perm)
{
throw new ProductForbiddenException();
}
return p;
}
public ActionResult Edit(int id)
{
User u = UserRepository.GetUserSomehowFromTheRequest();
Product p = GetProductForUser(id, u, Permission.Admin);
return View(p);
}
public ActionResult View(int id)
{
User u = UserRepository.GetUserSomehowFromTheRequest();
Product p = GetProductForUser(id, u, Permission.Access);
return View(p);
}
public override void OnException(ExceptionContext filterContext)
{
if (typeof(filterContext.Exception) == typeof(ProductForbiddenException))
{
// handle me!
}
base.OnException(filterContext);
}
}
You just have to provide ProductPermissionService.UserPermission, to return a user's permission on a given product.By using a Permission enum (I think I've got the right syntax...) and comparing permissions with <, Admin permissions imply Access permissions, which is pretty much always right.
You can use a XACML based implementation. This way you can externalize authorization and also have a repository for your policies outside of your code.

How might a site like Stack Overflow pass user information around in ASP.NET MVC?

Basically, I log into my website using OpenId, very similar to what I am assuming SO does. When I get the information back, I throw it into a database and create my "Registered User". I set my AuthCookie:
FormsAuthentication.SetAuthCookie(user.Profile.MyProfile.DisplayName, false);
Then I can use this for the User Name. However, I would like to pass in the entire object instead of just the string for display name. So my question is:
How does SO do it?
Do they extend/override the SetAuthCookie(string, bool) method to accept the User object, i.e. SetAuthCookie(User(object), bool).
What is the best way to persist a User object so that it is available to my UserControl on every single page of my Web Application?
Thanks in advance!
You can achieve this behavior by implementing your custom Membership Provider, or extending an existing one. The provider stores user information based on a key (or just by user name) and provides access to the MembershipUser class, which you can extend however you wish. So when you call FormsAuthentication.SetAuthCookie(...), you basically set the user key, which can be accessed be the provider.
When you call Membership.GetUser(), the membership infrastructure will invoke the underlying provider and call its GetUser(...) method providing it with a key of the current user. Thus you will receive the current user object.
Jeff,
As I said in a comment to your question above, you must use the ClaimedIdentifier for the username -- that is, the first parameter to SetAuthCookie. There is a huge security reason for this. Feel free to start a thread on dotnetopenid#googlegroups.com if you'd like to understand more about the reasons.
Now regarding your question about an entire user object... if you wanted to send that down as a cookie, you'd have to serialize your user object as a string, then you'd HAVE TO sign it in some way to protect against user tampering. You might also want to encrypt it. Blah blah, it's a lot of work, and you'd end up with a large cookie going back and forth with every web request which you don't want.
What I do on my apps to solve the problem you state is add a static property to my Global.asax.cs file called CurrentUser. Like this:
public static User CurrentUser {
get {
User user = HttpContext.Current.Items["CurrentUser"] as User;
if (user == null && HttpContext.Current.User.Identity.IsAuthenticated) {
user = Database.LookupUserByClaimedIdentifier(HttpContext.Current.User.Identity.Name);
HttpContext.Current.Items["CurrentUser"] = user;
}
return user;
}
}
Notice I cache the result in the HttpContext.Current.Items dictionary, which is specific to a single HTTP request, and keeps the user fetch down to a single hit -- and only fetches it the first time if a page actually wants the CurrentUser information.
So a page can easily get current logged in user information like this:
User user = Global.CurrentUser;
if (user != null) { // unnecessary check if this is a page that users must be authenticated to access
int age = user.Age; // whatever you need here
}
One way is to inject into your controller a class that is responsible for retrieving information for the current logged in user. Here is how I did it. I created a class called WebUserSession which implements an interface called IUserSession. Then I just use dependency injection to inject it into the controller when the controller instance is created. I implemented a method on my interface called, GetCurrentUser which will return a User object that I can then use in my actions if needed, by passing it to the view.
using System.Security.Principal;
using System.Web;
public interface IUserSession
{
User GetCurrentUser();
}
public class WebUserSession : IUserSession
{
public User GetCurrentUser()
{
IIdentity identity = HttpContext.Current.User.Identity;
if (!identity.IsAuthenticated)
{
return null;
}
User currentUser = // logic to grab user by identity.Name;
return currentUser;
}
}
public class SomeController : Controller
{
private readonly IUserSession _userSession;
public SomeController(IUserSession userSession)
{
_userSession = userSession;
}
public ActionResult Index()
{
User user = _userSession.GetCurrentUser();
return View(user);
}
}
As you can see, you will now have access to retrieve the user if needed. Of course you can change the GetCurrentUser method to first look into the session or some other means if you want to, so you're not going to the database all the time.

Resources