4

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

  1. Check whether a user is logged in.
  2. 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

1
  • Why not in a Cookie, even in the URL could do with a queryString AccountID=DGI36F2. You tagged Session-state so even using Sessions. I'm guessing you know these things back-to-front but can you give us a heads up which direction you want this question going? Commented May 15, 2012 at 6:23

2 Answers 2

2

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.

Sign up to request clarification or add additional context in comments.

3 Comments

Thanks for your answer @danludwig. I'm looking for an actual implementation on how/where to store this additional user data and some code examples would be helpful. I have the one-to-many model between users and accounts already as part of my L2S classes. So the part I'm unsure about is storing the selected account, reading it on subsequent requests while ensuring security.
+1 good call on the action filters. It's a feature of MVC that I often overlook.
Now that I have a good FilterAttributeFilterProvider for our IoC container, I use filters for all kinds of things. Makes the controllers a lot leaner, and is great for cross-cutting concerns like those mentioned in the OP.
0

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.

4 Comments

Does L2S use dynamic proxies? We had a problem trying to store (detached) entities in Session because of those. The actual runtime type is created by reflection, and is unique to the machine they are running on. So if we stored a (dynamic proxy) entity in Session, then one of the user's requests was routed to a different machine in the cluster, the other machine could not deserialize it because it had a different dynamic proxy class name. The dictionary solution here is an option, but you might want to DTO the entity data to a concrete non-entity type before putting it in memory-based storage.
@danludwig I have never used L2S. I've ran into the same issue with dynamic proxies in nhibernate. Sometimes I wonder the benefits are worth the headaches. DTOs are surely an options.
Does the URL parameter approach not create a HUGE escalation of privileges security risk? After a user has authenticated as themselves it is trivial to change the URL to access data meant for other users or to access administrative or higher privilege functions than their role would normally allow. URL tokens in general are also risky as the URL is exposed to any man-in-the-middle attacker even when using HTTPS.
@pwdst if you are ONLY using the url, then yes, that's a huge hole. The fact is anytime state is stored on the client it's at risk of being compromised.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.