Saturday, January 31, 2009

Creating Custom Membership and Role Providers

Introduction

In the Part 1 and Part 2 we learnt the concept and internal architecture of ASP.NET provider model. We know that the provider model is extensible and one can implement custom providers to suit his requirement. In this part we will develop two custom providers - one for membership and the other for roles.

Why develop a custom membership and role provider?

Well. There can be many reasons. Here are few:

  • You have custom data store (not SQL Server or Access) in which you want to store user data.
  • You are using some non-standard database for which there is no inbuilt membership or role providers.
  • You want to implement custom encryption mechanism for the data being saved and retrieved
  • You want to write database independent membership and role provider

One more reason that I have not listed in the above list is - You may want to use your own table schema instead of using inbuilt one. At first glance it may look odd but it can be a great way to save your work while migrating applications.

Requirements

Let's decide the requirements for building our custom membership and role providers.

  • We want to use our application database for storing membership and role information. That also means that we do not have a central database for storing membership details of multiple applications
  • We want to store membership details in a table called Users
  • We want to store available roles in the system in a table called Roles
  • We want to store user-role mapping in a table called UserRoles
  • For the sake of simplicity we will not include any encryption-decryption logic
  • User can register by supplying user name, password and email. No security question is required
  • We do not need features such as password reset and accounting locking

Database access

We will be using BinaryIntellect DatabaseHelper open source component for all our database access.

Creating the Web Site

To begin, create a new web site and add two classes called MyMembershipProvider and MyRoleProvider to App_Code folder. For the sake of simplicity we will be creating all the necessary classes in the web site itself. In a more real world situations you may create a separate class library project to contain these classes.

Configuring the web site to use our providers

Open the web.config file and add the following markup:



type="MyMembershipProvider"
connectionStringName="connstr"/>



type="MyRolesProvider"
connectionStringName="connstr"/>

Here, we instruct ASP.NET to use MyMembershipProvider class as membership provider and MyRolesProvider class as roles provider.

Creating custom membership provider

Recollect from Part 2 that custom membership providers need to inherit from System.Web.Security.MembershipProvider class. The MembershipProvider class in turn inherits from ProviderBase class. The MembershipProvider class contains several abstract methods that you must implement in your class.

If you are using VS.NET then your job is simple. Right click on the MembershipProvider class in the class definition line and choose "Implement Abstract Class". VS.NET will add dummy delimitations for all the required methods and properties from the MembershipProvider class. The following table lists all the properties and methods that you need to implement (methods are shown with parenthesis).

Property/Method Name Description
Initialize()* Receives the connection string name specified in the web.config file. You can use it to perform database operation in your class.
Name* Represents name of our custom provider
CreateUser()* Creates a user
UpdateUser()* Saves modified information about an existing user
DeleteUser()* Deletes a user
GetUser()* Gets a user as MembershipUser instance
GetAllUsers()* Gets all the users as MembershipUserCollection
ChangePassword()* Changes password of a user
GetPassword()* Retrieves password of a user. Used when implementing "Forgot Password" feature
ValidateUser()* Authenticates the user
EnablePasswordReset* Indicates whether the password can be reset by the user
EnablePasswordRetrieval* Indicates whether the password can be retrieved by teh user
RequiresQuestionAndAnswer* Indicates whether user should supply a security question and answer during registration
RequiresUniqueEmail* Indicates whether the email supplied during registration should be unique
ApplicationName Name of the web application. This name is used in case you are using a central database for storing membership data of multiple applications
MaxInvalidPasswordAttempts Indicates the number of times user can try to login to the system
MinRequiredNonAlphanumericCharacters Indicates minimum no. of non alpha numeric characters that the user must supply during registration and password change
MinRequiredPasswordLength Indicates the minimum length required for the password when user registers or changes the password
ChangePasswordQuestionAndAnswer() Allows to change user's security question and answer
FindUsersByEmail() Searches user database on the basis of email
FindUsersByName() Searches user database on the basis of user name
GetNumberOfUsersOnline() Returns total no. of uses that are signed in
GetUser() Returns MembershipUser instance representing a specific user
GetUserNameByEmail() Returns the user name on the basis of email
PasswordAttemptWindow Indicates the time span for multiple login attempts
PasswordFormat Indicates the format of password e.g.clear, hashed etc.
PasswordStrengthRegularExpression Indicates a regular expression to be used to check the strength of password
ResetPassword() Resets the password
UnlockUser() Unlocks the user account

In our example we will code the methods and properties marked with * above. The remaining members will simply throw a "Not implemented" exception.

The complete source code of our custom membership provider can be found in the download (MyMembershipProvider.cs). As an example CreateUser() method implementation is given below:

public override MembershipUser CreateUser
(string username, string password,
string email, string passwordQuestion,
string passwordAnswer, bool isApproved,
object providerUserKey,
out MembershipCreateStatus status)
{
MembershipUser user = new MembershipUser(Name,
username, providerUserKey, email, passwordQuestion,
null, isApproved, false, DateTime.Now, DateTime.Now,
DateTime.Now, DateTime.Now, DateTime.Now);
string sql = "INSERT INTO USERS(USERNAME,PASSWORD,
EMAIL,ISACTIVE) VALUES(@UID,@PWD,@EMAIL,@ISACTIVE)";
db.AddParameter("@UID", username);
db.AddParameter("@PWD", password);
db.AddParameter("@EMAIL", email);
db.AddParameter("@ISACTIVE",
(isApproved == true ? "Y" : "N"));
int i = db.ExecuteNonQuery(sql);
if (i > 0)
{
status = MembershipCreateStatus.Success;
return user;
}
else
{
status = MembershipCreateStatus.ProviderError;
return null;
}
}

Creating custom roles provider

Creating custom roles provider involves creating a class that inherits from RoleProvider class. The following table lists all the properties and methods that you need to implement (methods are shown with parenthesis).

Property/Method Name Description
Initialize()* Receives the connection string name specified in the web.config file. You can use it to perform database operation in your class.
Name* Represents name of our custom provider
CreateRole* Create a new role
DeleteRole* Deletes an existing role
GetAllRoles* Returns all roles as string array
RoleExists* Checks if role exists in the database
AddUsersToRoles* Adds users to specified roles
RemoveUsersFromRoles* Removes users from specified roles
GetRolesForUser* Returns all the roles for a specific user
GetUsersInRole* Returns all the users belonging to a specified role
IsUserInRole* Checks if a user exists in a specified role
ApplicationName Name of the web application. This name is used in case you are using a central database for storing membership data of multiple applications
FindUsersInRole Searches for users belonging to a specified role

In our example we will code the methods and properties marked with * above. The remaining members will simply throw a "Not implemented" exception.

The complete source code of our custom membership provider can be found in the download (MyRolesProvider.cs). As an example CreateRole() method is given below:

public override void CreateRole(string roleName)
{
db.AddParameter("@ROLE", roleName);
db.ExecuteNonQuery
("INSERT INTO ROLES(ROLENAME) VALUES(@ROLE)");
}

Testing our providers

There are four test web forms provided along with the download - Default.aspx, Login.aspx, RoleManager.aspx and UserRoles.aspx. The first two test the membership provider and the later two test the roles provider. We use essentially the same Membership and Roles classes of ASP.NET. These classes in turn call our custom provides to get the job done.

Summary

In this article we saw how easy it is to develop your own providers for membership and role management. You can extend the application to suit your needs. You can also add more security features such as encryption and password strength.



Note : this article is collected from the website all right reserved BinaryIntellect Consulting.

1 comment:

Anonymous said...

If you are looking for a database-independent Membership provider, check out 21 Providers for ASP.NET. It works with many of the most-common SQL implementations available today.