So I read about how implementing your own authorization routines are bad!
http://www.nashcoding.com/2011/02/05/using-the-forms-authentication-membership-provider-on-appharbor/
And I got scared, because I've been implementing my actions as such (example, preventing access to account details if authenticated user is not the logged in user)
public ActionResult DisplayAccount(int someid){
Account a = context.Accounts.Single(a => a.id == someid);
// currentUserId() returns userid from FormsAuthentication
if (!a.owner == currentUserId()){
/* Not Authorised! */
}
}
Which apparently means it will break if ASP decides to cache my action (so the action doesn't even get executed).
So I'm now looking into using AuthorizeAttribute to do what I need to do, which is
prevent access to an action if not authenticated
check if authenticated user has access to the retrieved resource
However whenever I think about it, I can't think about how to implement the 2nd point. Roles don't work because its on a site wide level, but within the application there users have roles as well (e.g. Owner, Moderator, Contributor, User etc.), and they only have these roles within their respective parts of the application (e.g. owner of thread, contributor to wiki, moderator of forum etc.)
I have run into several examples of overriding AuthorizeCore. I can sort of imagine creating multiple AuthorizeAttribute subclasses for each resource I have (luckily not many), But just by looking at it, does that mean I have to query the database everytime I hit that action to ensure that the logged in user should be able to access that data, then query the database in my action to get the model, instead of doing that in my query?
So my questions are
am I getting too worried about caching too much? Will any of the following happen
website caches user A details, which is rendered on user B's screen?
website caches admin version of a page (with edit controls), and normal user sees cached version?
Using AuthorizeAttribute is a given, but how do I achieve what I need to do in point 2 without having to hit the database prior to the Action? Or what is the best way to achieve it in any case.
Or do I only use AuthorizeAttribute to determine if the user is logged in, and do other checking logic in my action?
Anyway, I hope this post isn't treading on any old paths (I couldn't find anything on this that I found definitive)
Edit: I guess, if I don't enable caching this problem wouldn't occur, is this correct?
Edit: for now, I am going to going to use vanilla AuthorizeAttribute, then check resource level access in my actions, then make sure I don't use caching for any authenticated actions. Hopefully will get more answers for this over the week.
I used the following approach in a recent project, by creating a DataRightsAttribute that used an enumeration for each supported model type. It works by first extracting the id from the route data, formcollection or querystring. Then it queried up the model type determined by the enum, and did the appropriate check to see if the current user is authorized to access it.
Usage was like this:
[DataRights(ModelType.Customer)]
This was used alongside AuthorizeAttribute (which we overrided), and never noticed any problems with caching.
Related
I can restrict entire controller action access with [Authorize(Roles=...)] however, what about if I want to allow a user to for example look at the data of the other users in their own department (only).
Specifically what I mean is when I give a user the details of another user, the URL will be like this:
myapp.com/user/details/45
And they could quite easily just edit the 45 to be whatever they want. I want to restrict the access if the user the requested is not part of their own company (their company is currently in their session but that can be changed)
I can see a few different ways to do this, but I am just wondering what everyone's preference is based on experience. Here are some options:
1.Change the way access is done by editing the routing. myapp.com/Company/4/User/4' where 4 is a psudoID which references an actual ID internally.
2.myapp.com/OurCompany/User/4' < same but with a controller for ourcompany instead of generically companies.
3. Just check it internally by checking the requested user is in the same company: `myapp.com/User/42345'. My issue is that this has to be customized for each kind of request (users, products, related companies). But this is what I'm looking at right now.
EntityFramework actually has the power to reference the company members of the current user because when they log in I get their company entry and store it in the session. This allows easy access to the members of the company like so:
SessionHandler.UserSession.CompanyTable.UserTable.Where(e=> e.UserID == id).FirstOrDefault();
This seems odd to those who don't know about EF, but basically the subtable UserTable is a subset of all users referenced by the Foreign Key of the CompanyTable, which does indeed return the correct set of entities. The issue with this is that updating the entry in this way actually updates the entry in the SESSION but not inside the DATABASE, which is really bazaar to watch because it means the logged in users can seemingly make changes to the database but infact they are just being saved into the session. (staring at the database whist making these changes confirmed it, logging out and logging in nothing was saved, but db.SaveChanges() was correctly being called, just there were no changes to be made)
First, for the love of everything good and holy, stop putting that in the session. The session shouldn't be used for this, regardless, and it's code smell of the worst kind.
What you're talking about is object-level permissions. The correct way to do that is to limit your queries with some identifying factor of the currently logged in user. Your entities need a concept of "ownership" for this work. In other words, they need a foreign key to some other entity that "owns" that particular instance. Since here you want to restrict by department, that means all your users need to be owned by departments. Then, for situations like this you can do something like:
db.Users.Where(m => m.DepartmentId == user.DepartmentId)
Where user here is the instance of the currently logged in user. By querying in this way, the subset of entities available to the user is restricted by the user, itself. Which means attempting to access a URL with id outside of that set will naturally result in a 404. A 404 is actually the best thing for this, because even though it's authorization based, returning a 403 lets you know something is there - you just can't access it. For a hacker, this kind of information is half the battle. A 404 being returned gives you zero information. It might not exist or it might just not be available to you.
I have been learning how to use ASP.NET MVC4 and have been getting my head around authenticating users and user roles and posting data using the entity framework to SQL.
However I have not been able to find any guides/resources (maybe I don't know the correct term) for posting and retrieving data that is unique to an specific user. For example how would a user only see the entries that they created if it was a site that stored data that is personal to each user.
What patterns/designs does one use when trying to do this?
Creating a sandbox of data for a specific is usually tied to authentication. You can access this many ways through ASP.Net.
First of all, every user gets identified even if they never log in. They get a session identifier. It essentially creates a small place in memory for this user where you can store any user related information. Think of Sessions as walled gardens for each user.
Session["UserFullname"]
This works, but realize Session is limited by time, so it is very volatile. Utilize it, but don't depend on it.
The next method is to authenticate a User. This is done using Cookies, but usually handled transparently for you by ASP.Net Membership or other authentication providers. To access the authenticated User you just need to use a simple line in your Controller actions.
// User is the property
User.Identity.Name
Both these methods can store information about your user that you would use to query data specific to them.
Select * From Orders Where UserId = *User.Identity.Name*
Note that both Session and User are accessible through HttpContext.Current as well, as long as you are in a web environment.
HttpContext.Current.User
HttpContext.Current.Session
You won't need to access them this way unless you are not inside your Controller, or inside of another class that doesn't already give you access to the HttpContext. I don't recommend this way either, since your code could be used outside of a web application where HttpContext is not available.
I hope that makes sense, and please feel free to ask me questions.
This is not so much about mvc, but more about the problem of relating data to a specific user. You have to ask yourself, how would you identify a piece of data to a user.
The way you would do this is to tie the data to the user in the data store somehow.
In a relational database you would do this by having a User table and using the unique key on that table to insert data into another table such as Order. Order would then have a User Id.
When a user logs in, you could store that ID in session and use that to filter out orders based on the id.
I am creating a Grails website where users will have access to the resources they create. Till here everything is clear to me. I define ROLE_USER and lock down my controllers and actions using the Config.groovy file.
The issue I am facing is that I have requirement to support group of users such that some resources created by a user can be edited/updated/deleted by other users of the same group. How do I associate a user with a "group" in spring security, what is the design/library I should use here?
What you will need to do is to have your users' roles (the authorizations) come from the database. Once that is the case, you can then easily adjust the roles a user (or set of users) has and create/remove them on the fly. The docs have some pretty good info on how to get the roles to come from the database, so I won't go any more into that here.
Once the dynamic roles are in place, however, you still need to be able to connect roles to the objects that are created. There are essentially two ways you can go about doing this:
Access Control Lists
Custom logic
Depending on the granularity you need and the flexibility you want, one option may be more appealing than another.
Access Control Lists essentially allow you to have a permission mapping between each user and each entity instance. As you can imagine, it's a fair bit of overhead and can perform poorly if you have a large number of entities and users.
Putting together your own logic, on the other hand, is much more flexible because you can set up your own scheme to connect entity instances or entity classes to users and their roles.
I dont think that spring-security provides such functionality out of the box so you will have to do that manually.
For each domain class that you this kind of functionality, store the user name of current logged in user
def authenticateService
def user = authenticateService.principal()
entity.setUser(user?.getUsername())
Then in the update/delete method of the contoller you should check if the role of the current logged in user matches
the role of the user that created the entity. If you have a match you should proceed with the update/delete otherwise throw an exception
/redirect the user to an error page
As role you can use the spring security roles or you can create a property on the user object you have created
I have what seems like a common issue with SaaS applications, but have not seen this question on here anywhere.
I am using ASP.NET MVC with Forms Authentication. I have implemented a custom membership provider to handle logic, but have one issue (perhaps the issue is in my mental picture of the system).
As with many SaaS apps, customers create accounts and use the app in a way that looks like they are the only ones present (they only see their items, users, etc.). In reality, there are generic controllers and views presenting data depending on the customer represented in the URL. When calling something like the MembershipProvider.ValidateUser, I have access to the user's customer affiliation in the User object - what I don't have is the context of the request to compare whether it is a data request for the same customer as the user.
As an example,
One company called ABC goes to abc.mysite.com
Another company called XYZ goes to xyz.mysite.com
When an ABC user calls
http://abc.mysite.com/product/edit/12
I have an [Authorize] attribute on the Edit method in the ProductController to make sure he is signed in and has sufficient permission to do so.
If that same ABC user tried to access
http://xyz.mysite.com/product/edit/12
I would not want to validate him in the context of that call. In the ValidateUser of the MembershipProvider, I have the information about the user, but not about the request. I can tell that the user is from ABC, but I cannot tell that the request is for XYZ at that point in the code.
How should I resolve this?
Because Authorize is on the same thread as the request you could determine the subdomain by inspecting:
System.Web.HttpContext.Current.Request.Url.DnsSafeHost
Doing so on each call would certainly keep things in order, however this is a purely cosmetic check during authorization. I recommend that you simply look at this information during authentication. Once you know they are requesting XYZ and authenticate them into it, authorization should be only concerned about controlling features/data they have access to as XYZ. Their being from XYZ should be stored as part of the CurrentUser from that point.
I 'm writting an application in ASP.NET MVC. Basically I have some pages which require user authentication. Upon user logon, I keep the user row in a session. So in my controller I can access to the user.ID without making extra queries.
When project is in debug mode I can only change things in the views. Not in the controller.
If I 'm not debugging, I can build the solution, and see the changes I made without running the project (with F5). BUT, it looses all session variables I have.
So basically for every no-matter how small change in the controller, I have to logoff, logon to see my changes.
Are those normal behaviours?
Like Dan stated, this is normal behavior. To make it easier (and slightly more robust) is to change your code slightly. This is of course assuming that you are storing more than just the User ID in session since you can access the User ID via Controller.User.Identity.Name when they are authenticated. So you perform the lookup of the additional data in the session object and if it doesn't return null then use it. If it does return null, then look up the additional information again based on the User ID and store it in the session again. This is the approach I take for storing information from Active Directory and it works great.
Yes, this is a normal behaviour of ASP.NET as a whole not just MVC.
If you need to recompile (e.g a change in controller or business object) you will be in a new session when you run in debug. Like you say, only changes in views or pages (which do not require a recompile) will allow you to see changes in same session.
Kindness,
Dan
Recompiling will clear all the current session data. However it will not clear your authentication ticket, that's stored as a cookie, so there's a few things you can do to avoid this.
If you only need to access the user id then use User.Indentity.Name
If you only need basic user data for display purposes, such as the user's name, then you can store that in a session cookie. Warning: only do this for displaying data unless you encrypt the cookie data, plain text cookie data shouldn't be trusted.
If your user data is more complex than that then access the data through a method that uses caching as suggested by Agent_9191
Add something like this to a base controller or extension method
protected UserData GetUserData() {
UserData user = HttpContext.Session["User"] as UserData;
if (user == null) {
user = UserDataRepository.GetUser(User.Identity.Name);
HttpContext.Session.Add("User", user);
}
return user;
}