ASP.NET: Custom MembershipProvider with a custom user table - c#

I've recently started tinkering with ASP.NET MVC, but this question should apply to classic ASP.NET as well. For what it's worth, I don't know very much about forms authentication and membership providers either.
I'm trying to write my own MembershipProvider which will be connected to my own custom user table in my database. My user table contains all of the basic user information such as usernames, passwords, password salts, e-mail addresses and so on, but also information such as first name, last name and country of residence.
As far as I understand, the standard way of doing this in ASP.NET is to create a user table
without the extra information and then a "profile" table with the extra information. However, this doesn't sound very good to me, because whenever I need to access that extra information I would have to make one extra database query to get it.
I read in the book "Pro ASP.NET 3.5 in C# 2008" that having a separate table for the profiles is not a very good idea if you need to access the profile table a lot and have many different pages in your website.
Now for the problem at hand... As I said, I'm writing my own custom MembershipProvider subclass and it's going pretty well so far, but now I've come to realize that the CreateUser doesn't allow me to create users in the way I'd like. The method only takes a fixed number of arguments and first name, last name and country of residence are not part of them.
So how would I create an entry for the new user in my custom table without this information at hand in CreateUser of my MembershipProvider?

I think you should go on with your approach and add a new function in your implementation, I mean, overload the CreateUser method and have a CustomMembershipUser (that extends the MembershipUser) as a parameter.
In that way, before using the provider, cast it to your CustomMembershipProvider and use the overloaded method.

I agree with your analysis that you should keep both membership and profile information in the same table. Since you are correct that you are restricted by the number of parameters that CreateUser takes, you will need to design your field so that non-membership profile attributes are nullable. This does not mean that you will have required fields that are null in the database, however. Instead, you can you the below snippet:
string username = .../ retrieve username here
Membership.CreateUser(username , password, email);
ProfileBase newProfile = Profile.Create(username); //since the user has just been created, all properties will be blank
//set all entered properties
newProfile.SetPropertyValue("MyProp1", myProp1Value);
...
newProfile.SetPropertyValue("MyPropN", myPropNValue);
newProfile.Save();
In this way, you leverage ASP.NET's membership providers to create the user and save profile data, but to your end user it is a single atomic operation.

Related

Restrict users accessing other users' data

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.

MVC5 WebSecurity, use secondary database temporarily

I have a solution with two MVC projects. The first is a management system, the second is a portal for contractors. I want to manage the contractor login of the portal via the main application.
The membership database for both projects is identical in schema (the default). My thought was to hijack the main system's Register action, swap out the connection string, register the contractor user in its' own database, then swap the connection string back to the main one.
I'm guessing this won't work, namely based on the fact that WebSecurity.InitializeDatabaseConnection() can only be called once.
Thus my question: if I want to call the WebSecurity.CreateUserAndAccount() method but point it to a different database than what it was originally initialized for, how would I do so?
I looked at the built-in InitializeSimpleMembership filter for clues, but am not seeing the path I'm looking for.
Is this even a feasible approach?
Alternatively, if I can get the password hashed per the same way it's done by default, it looks like I can enter the required data to the db manually.
It appears I can manually work this out. According to the msdn docs for WebSecurity.CreateUserAndAccount():
This method creates a new entry in the user profile table and then a corresponding entry in the membership table. The ID of the membership entry is based on the ID of the user profile entry. (The IDs of the entries in the two tables match.)
(https://msdn.microsoft.com/en-us/library/webmatrix.webdata.websecurity.createuserandaccount(v=vs.111).aspx)
Inspecting the database schema shows that I only need two tables for this, and the fields are straigtforward.
To hash the password according to the same way the SimpleMembershipProvider does, I can use the System.Web.Helpers.Crypo.HashPassword() method.
This simplifies the basic account creation considerably as I don't have to piggyback off the AccountController and can just write these directly to the db and call it good (for my purposes, anyway, not requiring verification tokens and having seperately handled email notifications). On the portal side, the built-in SimpleMembershipProvider should be able to handle everything as usual.

Is it possible to implement user profiles without using SimpleMembership?

I need some advice. I'm currently using MVC 4 & SimpleMemberhip with LDAP to authenticate users. The issue is, I don't want to store their usernames and passwords in the
webpages_Membership table due to security concerns. The second issue is I want to provide user-editable profiles.
Here's what works so far:
User logs for the first time and a new entry is created in webpages_Membership
An individualized link to edit the user profile is displayed on the homepage
Username is added to the UserProfiles table when profile is accessed for the first time
Certain user details are fetched from LDAP server and written to profile
Users can then customize their profiles
I'm currently using SimpleMembership with an override to the ValidateUser method. Everything works as it should but I don't need to store the LDAP usernames & passwords. Can this be done?
p.s. I know there is a better way to create new users & profiles besides on first time log in but I'm still working on it.
If you don't want to store the passwords (which SimpleMembership would do by default), you are better off deriving your own custom provider from ExtendedMembershipProvider (or maybe from SimpleMembership, but that would get complex) and write the LDAP implementation, or using one of the ones on NuGet. There's no built-in LDAP support in SimpleMembership, so any approach you do would be a nasty hack which will probably bite you later on.
As for the UserProfile, it doesn't sound like your requirement is that different to the usual UserProfile use case - create custom properties on the UserProfile model, update the database accordingly, and build a UI to allow the user to edit whichever of those properties they should be able to directly edit.
(edit)
Footnote. In my answer to "How do I use my own database with SimpleMembership and WebSecurity? What is MVC4 security all about?" I examine the history of membership, how ExtendedMembershipProvider fits into this, and how the new classes such as WebSecurity work on the basis of a provider being a concrete implementation of ExtendedMembershipProvider (which SimpleMembershipProvider is, for example). For anyone looking to derive their own provider to use with WebSecurity, that answer is worth reading.
I've managed to bypass storing user details in the Membership provider by creating the required tables with Code First. I'm now able to create new users and store them in the UserProfile table.

How can I add relationships to the aspnetdb database in MVC?

I'm having a hard time wrapping my head around how to use the Memberships in MVC. I know there is the built in ASPNETDB database which has all the basic tables for users and such. But what if I wanted to add a relationship between one of my custom tables and this built in user table?
If I had a database table that contained blog comments, we'll call it Comment. And each Comment had a userID associated with it could I do something like?
User.Comments.Add(someCommentObj)
Anyone know of a good article on this? Is this even possible?
Thanks
Have a look at this extensive article on the MembershipProvider:
https://web.archive.org/web/20211020202857/http://www.4guysfromrolla.com/articles/120705-1.aspx
Look at Part 6 and 7, you'll probably want to implement a custom ProfileProvider and store the comment reference in the Profile.
Part 6 - capture additional user-specific information using the
Profile system. Learn about the
built-in SqlProfileProvider.
Part 7 - the Membership, Roles, and Profile systems are all build using
the provider model, which allows for
their implementations to be highly
If you want to use your own custom membership tables then you'll need to build your own MembershipProvider. Matt Wrock has a walkthrough:
You'll notice that the default AccountModel allows you to inject your own provider:
public AccountMembershipService(MembershipProvider provider)
{
_provider = provider ?? Membership.Provider;
}
Nerdinner has an example of dependency injection that you would probably find useful:
Warning Here are two solutions that will work. The first one is easy. The 2nd one, I think is what you're after, but take it for what it's worth. Make sure you realize what you're doing, since this will take the membership provider data and access it directly, which could result in some hidden bombs if you're not careful (like deleting data).
The membership data is meant to just be used for authenticating; roles for authorizing; profiles for user speicific data (like time zone or favorite color.
Solution One
If you wanted to add a comment under the current user (or any user) you could do:
var comment = new Comment(....);
comment.userId = User.Identity.Name; //for user name
or
comment.userId = new Guid(Membership.GetUser().ProviderUserKey); //for guid in table
That's the eays way and you never really have direct access to the aspnet tables, you just use its info.
Solution Two
I'm assuming that you're using L2S and the designer in VS.
By adding the membership table(s) to your L2S design, you will get access to its data. This may even be preferable for some quick querying (like dates, lock out info, etc. since you don't have to use the built-in sprocs which have some serious over-kill and heavy code). If you create a relationship in the DB or in the L2S designer, you'll have a relationship that you can access like your question asks.
At this point, the designer has created your classes for comments and users. If you do anything with the actual user table you just created, you're circumventing the membership provider's design - don't do this unless you now what you're doing. When you add a comment to the User, it will add the comment to the comments tables with the correct relationship intact.
You should now be able to do:
var user = MyUser.GetById(userId);
user.Comments.Add(comment);
Remember, that the User in this case is different than when you do
var user = Membership.GetUser(userId);

Extending MembershipProvider

Scenario:
Building an application for companies to enter information into.
I need to extend the built-in membership provider in asp.net. My unique situation is that I already have demographic information for each company, but NOT userid's and passwords for a web app. I want to prepopulate the DB with the demographic information, send each company a unique pin number, and ask them to register with my application. I want them to create a userid, email address and password, after they've "validated" who they are by entering the pin number that they received in the mail. Yes, we'd be sending them, via snail mail, a unique pin number.
Problem:
Not quite sure where to start. The problem centers around the fact that we already have demographic information for these companies and right now, it's not possible to use this new web application to update the demographic info, so we really don't want them to change it. We also don't want just anybody being able to register an account and entering demographic information.
Questions:
Am I thinking about this the wrong way?
Is extending the membership provider the right way to go?
What is the best way to handle this situation?
Extending or implementing a membership provider is quite straightforward actually, and sounds like a good approach for your problem.
The only functionality of membership providers that most applications even use is the ValidateUser method. In your case, you would simply override this method to fetch the customer name and pin from the database and match it with the user's input. If it matches, return true, otherwise, false.
Create a new class, inherit MembershipProvider. Right click the class name and implement the abstract methods, and you're off to the races. There are lots of walkthroughs and examples if you google for them as well.

Categories