In creating a new ASP.NET MVC application, I have an issue with the approach I'm using to store user-created data for temporary users who have yet to create an account which I then try to convert to a real user. That probably doesn't make much sense, so let me explain:
A visitor to the site can enter profile settings before being made to register with a username, password, etc.
I'm creating database entries via LINQ to SQL for a new user in this case, using the Request.AnonymousID value as a temporary username.
When the user chooses to register, I need to 'switch over' the relevant database records to use the newly entered username instead of the temporary one.
The problem is that when I try to update the record I can't because the username is the primary key, so I'm forced to delete the record and add a new one...
I can probably persevere with this, but I think I might just be going about this in completely the wrong way and wondered if anyone could suggest a better way to allow visitors to store information before they've registered and have that carry over when they do.
I know about profiles but want the profile information to be available to other visitors. I also know that I can create an anonymous profile but it seems like I should be able to keep the data model out of the web.config file.
I would suggest having an independent primary key for the table with your custom user data.
And then have fields like RefAnonymousId and RefUserId to relate that user data to the anonymous user and the registered user, respectively.
For example:
TABLE UserData
(
UserDataID int IDENTITY(1,1) PRIMARY KEY,
RefAnonymousId uniqueidentifier,
RefUserId uniqueidentifier,
... (data fields),
(maybe also unique keys on RefUserId and RefAnonymousId)
)
That way you will also be able to identify the user when the user is logged out and maybe automatically log the user in...
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.
Users in my database have a non-unique display name; the only unique identifier is the UserId.
Normally, I would add a user to a Role using the following:
Roles.AddUserToRole(user.Username, role);
But now I don't have usernames, so I need to re-think all work related to Roles.
One messy option I can think of is to copy the Id of every user into the Username field just to satisfy SimpleMembershipProvider... though I'd rather somehow use extension methods to handle this if it's even remotely advisable and possible to do so... just so I don't have to clutter my Users table with a bogus column.
Any help here would be much appreciated.
Update:
Even if I copy the userId to the username column to get SimpleMembership working, I still need a username to create the user:
WebSecurity.CreateUserAndAccount(model.UserName, model.Password, etc)
So I'm at a loss of what to do, aside from rolling my own Membership.
Update again: I just realized I can pluck my email field and just use the Username column to store unique email addresses. I'm still interested in hearing how else this could be addressed.
You need something unique the user can enter to identify themselves during authentication, whether it is a username they user came up with, an email address, PIN, etc... Having the user remember a number generated as the unique identifier for the UserProfile does not seem like a good user experience. So whatever is used to uniquely identify that user, that is used during the log in process by the user, can be stored in the username column, if you do not want to add any additional columns. From the latest update to the question it looks like email address is being used to identify the user and you could store this in the username column without issue.
Adding columns to the UserProfile is easy to do and I would not shy away from it if it provides a better user experience and security. For example, if you want to capture a display name to display on the web site or in any communications with users you could add an email column and tell SimpleMembership that you want to use that to identify the user by changing a parameter in the WebSecurity.InitializeDatabaseConnection Method. You would change the parameter userNameColumn to be the name of your email column.
yes, either use the Ids as username or if the email is unique as username.
It is not a messy way btw with this situation. I would go with the id, if no one will eversee the userId.
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 building an MVC4 application and need to use external authentication (Facebook, etc). I have that working fine. I see the SimpleMembershipProvider creates 2 separate tables to handle this: "webpages_Membership" and "webpages_OAuthMembership". I need to map other tables (foreign keys) to User ID. The problem is both tables have a user ID which are unrelated to each other.. I could ignore the foreign keys in the database if necessary (don't like that though), but the bigger question is this: in code, how would I determine whether a user is authorized externally or locally so that I know where to get his other info from? Specifically, I have a user profile table where I will map other attributes to.. having 2 different types of user, how does one go about this?
Right, I have learned more about MVC4 and SimpleMembershipProvider and I see now that the User ID on both tables is actually a foreign key to a parent table called UserProfile. So, User IDs will still be unique.. it's just the mappings are a little different to what I was accustomed to with the standard ASP.NET membership provider. Email address and whatever other properties are required can be easily applied to the UserProfile table.
I want to build and maintain a table of users. All users that access the ASP.NET MVC site are authenticated via Windows Authentication so they're bound to have a unique username. I'm grabbing the user name from:
System.Web.HttpContext.Current.User.Identity.Name
I feel like I could go two ways with this.
Anytime the user table or any tables that references the user table are accessed, I could add the user if it doesn't exist. I'm worried this might be very error prone if user's existance isn't checked.
Anytime the user visits any page on the site, check if that user exists in the db and if they don't exist, add the user. This may have a lot of overhead as it'll be checked every page change.
I'd like to hear which of these is the better solution and also how to implement them.
I think a better way would be something similar to the option two.
Anytime a user visits a page, check a session variable to see if that user was checked against the DB. If the session variable is not there, check if that user exists in the DB, add the user to your table if necessary, then set the session variable.
That way you don't have to hit the DB on every request.