Similar questions have been asked before but I'm still without an answer and have spent a decent amount of time trying to find one.
The scenario is this. We have an ASP.NET MVC application which is using Forms Authentication / LINQ-to-SQL data model.
Each user can be associated with 1 or more Accounts. Upon login, the application checks how many Accounts they're associated with.
0 = Go to error page and let them know they have no access
1 = Set The User Data to use that account
2 or more = Go to a page which allows them to select which account they'd like to use (with ability to change during their visit)
How would you store this information?
Furthermore, I'd like to use this Account as the base for my controller actions. i.e. Data on subsequent pages they visit will be related to the Account they've selected.
Singleton cough comes to mind but am unsure how to implement that.
An approach which I'm currently investigating is a Base Controller that all controllers will inherit from that will
Check whether a user is logged in.
If so, check whether they have an Account Selected
No - redirect them to Account Selection page
Yes - proceed with original request
What is a recommended/best-practice way of doing this?
Thanks
Marko
Don't use a base controller. You can accomplish this using action filters. This will give you the point of intercept for checking whether they are logged on, whether there is an account selected, and even redirecting them to the error or account selection page. All you have to do is set filterContext.Result during the action filter's OnActionExecuting method to prevent the request from going through. You have complete access to session, temp data, cookies, and the whole HttpContext during the action, just as you would in the controller. Also you can property inject dependencies using a filter attribute filter provider, so that should give you any data access you need.
As far as modeling the data, I am not too familiar with Linq to SQL, but I think a normal one to many should do the trick:
User (1) <-------> (0..*) Account
public class User
{
public virtual ICollection<Account> Accounts { get; protected internal set; }
}
public class Account
{
public int UserId { get; protected internal set; }
public virtual User User { get; protected internal set; }
}
Update: Sorry, I misunderstood what you meant by "store". In MVC, there are only a few ways you can store it - Session, Cookie, and Cache (and TempData, which is just short-term session) are the most popular choices. My choice would depend. Session is probably the easiest, but if you are deployed to a server farm with a load balancer, you need to think about what would happen if the user's session jumps physical machines. Will the session remain intact?
As Jeremy says there is also cookie. No worries about load balancing here, but the semantics are a little harder to deal with than session. For example you have to send a redirect to write the cookie before you can read it, and I've never liked the fact that you have to add an expired cookie to delete one. Since this data is part of your security, you also may want to encrypt the cookie value.
If you use cache, the data will most likely still end up living in memory somewhere, like session (unless you are using a SQL session provider). Cache would probably be my last choice, since we use Azure and their cache doesn't support a lot of great MVC features. You also have the same problem with load balancers moving the user to a different machine in the cluster, where the data may have to be re-cached.
Either way, you should still use action filters for this instead of a base Controller class.
What type of scale are you talking about? Are we talking about a million users or a couple thousand?
My first thought is to create a dictionary with the key being the login username (assuming it's unique) and the value to be an array of associated accounts(key or all the data). I would then put the dictionary into cache. Expiring it whenever a new association is created.
There are a couple of problems with this approach. First, how fast are new associations being created? If they are constantly being created, then the cache is a moot point. You'd always being going to the DB. Second, if you have millions of users/associations putting all them into cache may not be practical.
Another possibility is a session state server, this would be solely dedicated to storing the relationships.
Yet another possibility is querying the database each time, depending on the size of the data set this would work. When the data set grew to a size where pulling real time data each time is not practical you could architect a solution that fits your needs.
As for persisting the selected account between requests, The options are either cookies, url or database(could be a field on the user i.e. CurrentAccount, this approach is a bit of a kludge).
Since you are using MVC I'd use the URL, with routing and a custom route constraint you could create a url that contained the account. Since the user has to login, you already know the user identity.
Example: http://www.acme.com/{account}/edit/
Checking if a user is logged in could be handled in an action filter or Application_AuthenticateRequest in the Global.asax. Authentication is fairly well implemented in ASP.NET. Heck a lot of it is driven by configuration values in the web.config.
After the authentication has been confirmed the account redirection could take place in the same method. Or you could wait and check in an action filter.
Related
My Problem:
My app has number of pages/resources that should be accessible by guest users but only after providing correct pair [Resource Code - Unique Token] (each page has one Page Code and multiple unique "Tokens" issued for each user). Tokens are generated beforehand and stored in DB. After accessing the page, user will be able to interact with multiple other resources belonging to particular page.
How I organized this so far:
Page asks user to provide token and checks it with records in DB. If this is a correct token for resource requested, it writes cookie and then, every time user interacts with the resource or its content, controller will every time read cookie and check [PageCode-Token] pair with database before continuing the action.
Question:
Is there any other, more elegant and efficient approach? Should I use Session instead? I feel a bit bad about querying DB every time.
This depends on how many users access your service, if the volume is too large it would be recommended to create a cache where all tokens are stored, thus avoiding a database overload. However if the service is not widely used this is not necessary as a database can handle a lot of requests.
You could create a cache in two ways, using ready-made software or create a small cache within the project itself.
If you choose to use software, I would recommend Redis, it is a cache database that stores values with or without a timeout, ie after a while the tokens are deleted.
Keep in mind that this does not prevent you from making requests to the database, but you will always make requests to the cache first (Redis) and if the value does not exist, it is necessary to search within the database.
But if you choose to create your own, you will need to do most things manually and always knowing how much resources can be allocated. It may be more advantageous to use software than reinvent the stone.
I'm having a problem of designing a common functionality of hiding information created by a user from other users. As an example,to edit Products created by USER1, normally we use one of following.
/Product/Edit/Id/1
/Product/Edit?Id=1
My concern is, if USER2 got the Id, 1, he also able to access Product with Id=1, which was created by USER1. How to restrict USER2 accessing USER1'S data? This may needs to apply for every module in the project. Is there a common way to achieve this? Thanks
If you are keeping state of what user is accessing the data. You can add a "WHERE CreatedBy = {YOURLOGGEDINUSER}" to this query and throughout your application. Then even if he gets the ID correct no data would be returned.
Assuming that you have enabled some sort of ASP.Net Authentication (The user has proved who they are) then you now need to think about the Authorization (what the user is allowed to do).
It doesn't help that these two terms are often combined or used interchangeably. In MVC a custom AuthorizeAttribute is often used to do both.
For managing records, the current logged on user is accessed via the IPrincipal from HttpContext.Current.User.
The user id is usually set at HttpContext.User.Current.Identity.Name although you may need to do a null check if not every route is authenticated.
I am trying to add an item to the users Identity, but I think this could also be done another way.
I have added a few custom fields to the User Identity, but what I am trying to do is once the user has successfully logged in, I check if they are assigned to multiple branches(Store Locations) and then offer a list for the user to select which Branches they want data to be displayed for.
I have done this successfully, but now I want to store each of the branches in the users session/ cookie/ Identity (not sure what terminology to use). The user is displayed a number of checkboxes, and once they click save, I am unsure of what to do.
I need to save it in this function:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult SelectBranch(params string[] branchNames)
{
foreach (var branch in branchNames)
{
//add branch to session/cookie/identity
}
return RedirectToAction("Index");
}
What steps do I need to take to be able to do this?
It depends on what you end goals are. Should the choice of branches be persistent or transient? Based on your comment, it sounds like you're wanting to save it for the current session, but then make the user choose again the next time they come back. If that's what you application truly needs, fine, but it seems to me that something like this should either be saved permanently, with the ability for the user to update those choices when they see fit, or completely temporary, i.e. for the current request, such as filtering a set of results by a branch or branches.
Storing for only the current browser session begs the question of whether the user will realize that their choices are being persisted across additional views, and conversely, whether they'll understand why they have to continually set branches each time they come back to the site.
That said, your choices:
Permanent - Store on user entity. Add a M2M between ApplicationUser and StoreBranch (or whatever).
Transient - Set in Session.
Technically, you can use claims, but they aren't really a good fit for this. Claims should be used when either 1) the data being stored is not applicable to every user or 2) the data being stored is transient in nature, but should survive the session. A good example use of a claim would be storing an auth token from something like Facebook. Not every user will necessarily have a Facebook auth token, so it's not something you would want as a database column, and there's generally a lifetime for that auth token, making it temporary but still something you would need to hold on to even if the session was destroyed.
Based on your description of what you want to achieve, Session is your best bet, but I would argue strongly that there's probably value in persisting this choice permanently, as long as the use can alter it later. To save it to the session, I would literally just save your string array directly to the session (no need for the for loop):
Session["BranchNames"] = branchNames;
I would tell you to use Identity claims
What is a Claim?
A Claim is a statement about the user makes about itself, it can be user
name, first name, last name, gender, phone, the roles user assigned
to, etc…
You can know more about them here
I've got a Controller, and in one of the ActionResults, there is a javascript value being returned that I can access via:
Request.QueryString["frequency"]
But I need to use that same variable in another ActionResult. How can I do this?
I know that I can't set a
string frequency = "";
at the top of the Controller, and then just set in the 1st ActionResult.
When you say ActionResult, I assume you mean your Action methods within the controller that return Actionresults? e.g.:
public class HomeController : Controller {
// GET: /Home/
public ActionResult Index() {
var freq = Request.QueryString["frequency"];
// ** Want to persist freq here **
HomeIndexViewModel model = FooLogic.BarIndex();
return View();
}
// GET: /Home/Detail
public ActionResult Detail() {
var freq = **Want to access freq here**;
HomeDetailViewModel model = FooLogic.BarDetail(freq);
return View();
}
}
As your question refers to ActionResult, this could be any kind of ActionResult - without knowing exactly what you are doing this answer outlines a general approach to choosing a state persistence store.
The key questions to ask yourself are:
who will need to see this value - just the user who's request generated the value, or other users as well,
if user specific, are they logged in, do they have a session?
how long will they need to be able to see it for (the next request only, or maybe the next day)
where will they expect to see it (just in the current browser, or in another session)
Your options for data persistence are many and varied, each fulfilling a slightly different role, though many overlap with others in their potential use (e.g. Session and TempData). I've listed many of them below, all of which could solve your problem depending on the exact scenario. The first two (View-persisted data or TempData) are most likely to be useful for you, but in the absence of more information others may actually be the ones you need.
There used to be at least Nine Options for Managing Persistent User State in ASP.NET, and many of those still apply in MVC.
ViewData and ViewBag
Available to: current user within the current request, but you can use it to "store state" within the generated html ready to be passed to future requests
Only relevant if your ActionResult is actually a View (rather than a redirect, or another ActionResult like a FileStreamResult)
Lets you pass data from the current controller action into the current view being generated meaning you can insert it into client side objects that could send it back in the next request such as:
hidden form fields;
query string parameters for the next request; or
javascript variables for ajax requests.
An example of this would be to pass your freq variable into the View for the Index method (for instance by using ViewBag.Frequency = freq;, and then use it with #Html.ActionLink. E.g.:
#Html.ActionLink("Click for the Next Action", "Detail", "Home",
new { frequency = ViewBag.Frequency }, null)
Your Detail action then becomes:
public ActionResult Detail(int frequency) { ...
Similar approaches would allow you to use the value in hidden form fields, or in javascript for future AJAX requests by setting a javascript variable using var freq = #Html.Raw(Viewbag.Frequency);, for instance.
Note: There is a school of thought (which I think is a good one) that you shouldn't be using these, instead you should create a strongly typed class (such as the above HomeIndexViewModel) for the Model for each view to allow you to better test your Controller Actions and Views.
Advantages of ViewData/ViewBag:
It maintains statelessness on your server, so that you don't have to worry about worker processes being recycled, the next request going to a different server in your web farm etc.
It is potentially "back button proof" as your "state" sits in the page that has been rendered
Disadvantages:
Only useful Actions that render html
Not all data should be back-button proof, some data should constantly evolve and therefore stored as server-side state (e.g. a page hit count)
TempData
Available to: Current User, within this and the next request
By default (in MVC 4) this is implemented by SessionStateTempDataProvider (see the documentation)
It is really meant to be used when your ActionResult is a redirect to another Action, that way you know the exact scope and lifetime of the data you have stored.
Ostensibly this is designed to do exactly what you want, but there are considerations.
It relies on session state, so works for web farm and web garden scenarios only if you have configured Session state appropriately.
Session state may also not persist between worker process recycles depending on your setup.
You also have to worry about what happens in a "back button" or F5 scenario as the data may not be available a second time.
Session
Available to: Current User, within the current session for them. Scope also depends on how you have configured session state (e.g. to be local to the application domain, or database backed and available across the web farm)
This has all the same considerations as TempData, but you choose when to delete the value from the Session. It is really intended for general information relevant to the current session (e.g. a simple shopping cart that the user wouldn't expect to see if they close and reopen the browser, or visit the site on their mobile phone later on).
Cache
Available to: All Users, but only within the current application domain (so beware worker process recycling, web farms, web gardens etc.)
You can access this through the HttpContext property of your Controller. E.g.:
HttpContext.Cache["Frequency"] = freq;
Cookies
Available to: Current user, but only from the browser they used for the original request
Cookies are often overlooked as state persistence - but they are very useful for some kinds of data. Remember that if the user expects to see data associated with a user identity, then the cookie won't help if they log in on another computer, use an Incognito/Private browser session etc.
Database
Available to: All users, or just the current user, for as long or short a time as you like - you choose.
Databases are the daddy of state persistence. Your application code should be viewed as volatile, and able to deal with restarts, web farm scenarios etc. etc. If you want to store data, use a database. By "Database" I mean a data persistence medium in any form, from SQL Server to Redis, Azure file storage, Azure table storage, Amazon S3 etc.
Other options
There are other options, they are not commonly as commonly used though. For instance, you can implement your own caching (example here), or use ...
Static or singleton classes
Available to: all users, all requests to that worker process on that server
Data will persist within a single worker process only (so this has implications for web farms and web gardens), and only until the asp worker process restarts
Thread-safety is still a concern, but at least you can encapsulate thread-safety logic within that class
Very rarely useful due to the coupling to the lifetime of the worker process, and to the single server
What not to use
Controller class fields (instance)
Available to: current user, current request only
(Disclaimer: I believe that a new Controller is created for each request in all versions of MVC, but if that is not the case then you would never use these to store state)
Theoretically you'll never use these (unless this is a 10-minute application for a demo to your colleagues):
As instance fields on a class persist only for the duration of the class (therefore the duration of the current request) if you want to store state (data) for the request you should be using variables within your Action method to better convey intent/manage scope of the objects/values.
So if you are using instance fields in your controller, you are probably sharing data for controller methods you are calling from your Action.
This means you are probably using your controller to enact business logic.
Current best practice dictates that Controllers should be a collection of Actions that call business logic (including any generation of view models), (thin, not fat, controllers).
Ergo: Controller class instance fields indicate you should restructure your code
The times you need instance fields on a controller are when providing common services to all actions, such as IoC interfaces, but these are not storing state within or across requests.
Controller class fields (static)
Available to: all users, all requests to that worker process on that server
Not a good idea - static fields will be available to all users on all threads, so you have to worry about thread-safety. There are better state stores available out of the box if you want to share data between all users, such as Cache
HTTP is stateless, every request has it's own state and Controller instance.
You can use TempData which use Session but delete the value after you read it.
This is a beginner pattern question for a web forms-over-data sort of thing. I read Exposing database IDs - security risk? and the accepted answer has me thinking that this is a waste of time, but wait...
I have an MVC project referencing a business logic library, and an assembly of NHibernate SQL repositories referencing the same. If something forced my hand to go and reference those repositories directly from my controller codebase, I'd know what went wrong. But when those controllers talk in URL parameters with the database record IDs, does it only seem wrong?
I can't conceive of those IDs ever turning un-consumable (by MVC actions). I don't think I'd ever need two UI entities corresponding to the same row in the database. I don't intend for the controller to interpret the ID in any way. Surrogate keys would make zero difference. Still, I want to have the problem because assumptions about the ralational design aren't any better than layer-skipping dependencies.
How would you make a web application that only references the business logic assembly and talks in BL objects and GUIDs that only have meaning for that session, while the assembly persists transactions using database IDs?
You can encrypt or hash your ids if you want. Using session id as a salt. It depends on the context. A public shopping site you want the catalog pages to be clear an easily copyable. User account admin it's fine to encrypt the ids, so users can't url hack into someone else's account.
I would not consider this to be security by obscurity. If a malicious user has one compromised account they can look at all the form fields, url ids, and cookie values set while logged in as that user. They can then try using those when logged in as a different user to escalate permissions. But by protecting them using session id as a salt, you have locked that data down so it's only useful in one session. The pages can't even be bookmarked. Could they figure out your protection? Possibly. But likely they'd just move on to another site. Locking your car door doesn't actually keep anyone out of your car if they want to get in, but it makes it harder, so everyone does it.
I'm no security expert, but I have no problem exposing certain IDs to the user, those such as Product IDs, User IDs, and anything that the user could normally read, meaning if I display a product to the user, displaying its Product ID is not a problem.
Things that are internal to the system that the users do not directly interact with, like Transaction IDs, I do not display to the user, not in fear of them editing it somehow, but just because that is not information that is useful to them.
Quite often in forms, I would have the action point to "mysite.com/messages/view/5", where 5 is the message they want to view. In all of these actions, I always ensure that the user has access to view it (modify or delete, which ever functionality is required), by doing a simple database check and ensure the logged in user is equal to the messages owner.
Be very very very careful as parameter tampering can lead to data modification. Rules on 'who can access what ids' must be very very carefully built into your application when exposing these ids.
For instance, if you are updating an Order based on OrderId, include in your where clause for load and updates that :
where order.orderid=passedInOrderId and Order.CustomerId=
I developed an extension to help with stored ids in MVC available here:
http://mvcsecurity.codeplex.com/
Also I talk about this a bit in my security course at: Hack Proofing your ASP.NET MVC and Web Forms Applications
Other than those responses, sometimes it's good to use obvious id's so people can hack the url for the information they want. For example, www.music.com\artist\acdc or www.music.com\arist\smashing-pumpkins. If it's meaningful to your users and if you can increase the information the user understands from the page through the URL then all the better and especially if your market segment is young or tech savvy then use the id to your advantage. This will also boost your SEO.
I would say when it's not of use, then encode it. It only takes one developer one mistake to not check a customer id against a session and you expose your entire customer base.
But of course, your unit tests should catch that!
While you will find some people who say that IDs are just an implementation detail, in most systems you need a way of uniquely identifying a domain entity, and most likely you will generate an ID for that identifier. The fact that the ID is generated by the database is an implementation detail; but once it has been generated it becomes an attribute of the domain entity, and it is therefore perfectly reasonable to use it wherever you need to reference the entity.