Meditation and Mindfulness for Software / IT Professionals. Conducted by Bipin Joshi in Thane. Read more...

User Administration Tool (Part 2)

In the Part 1 of this series we started developing a web user control that allows us to manage various aspects of user administration such as password recovery, role mapping and profile management. We configured the database and web site for availing membership, role and profile features of ASP.NET. Continuing our development further we will now code various pieces of the functionality.

Binding the GridView with list of users

ASP.NET provides an inbuilt member called Membership through which membership information can be retrieved. In order to retrieve a list of users and bind them with the GridView we write a method called BindGrid(). There are two overloads of this method. One accepting the search criteria and search by option and the other without any parameters. Figure 1 shows these two overloads.

private void BindGrid(string criteria,FindByOptions option)
{
MembershipUserCollection m =null;
if (option == FindByOptions.Email)
{
ViewState["emailcriteria"] = criteria;
m = Membership.FindUsersByEmail(criteria);
}
else
{
ViewState["usernamecriteria"] = criteria;
m = Membership.FindUsersByName(criteria);
}
GridView1.DataSource = m;
GridView1.DataBind();
}
private void BindGrid()
{
if (ViewState["emailcriteria"] != null)
{
BindGrid(ViewState["emailcriteria"].ToString(),
 FindByOptions.Email);
return;
}
if (ViewState["usernamecriteria"] != null)
{
BindGrid(ViewState["usernamecriteria"].ToString(),
 FindByOptions.UserName);
return;
}
MembershipUserCollection m = Membership.GetAllUsers();
GridView1.DataSource = m;
GridView1.DataBind();
}

Figure 1: Binding GridView with a list of users

The first overload takes two parameters. The search criteria is a string containing the search pattern. The second parameter is of type FindByOptions. FindByOptions is an enumeration defined by us and looks like this:

public enum FindByOptions
{
    Email,UserName
}

Through the enumeration we specify whether we want to find users on the basis of their email address or user name. Depending on this parameter we call FindUsersByEmail() or FindUsersByName() method of Membership object. The return value of FindUsersByEmail and FindUsersByName  methods is a collection of type MembershipUserCollection. Each member of this collection is of type MembershipUser and represents a user. The MembershipUserCollection acts as a data source for the GridView. Note that the code creates two ViewState variables - emailcriteria and usernamecriteria - to store the corresponding search criterions. This way we can filter the users across post backs. This overload of the BindGrid() method is called when the administrator clicks on any of the Go buttons of the Find Users panel.

In the second overload of the BindGrid() method the code checks for existence of the same two ViewState variables. Their presence indicates that some search filter is active. If any search filter is active then the previous overload of the BindGrid() is called. Otherwise the code calls the GetAllUsers() method of Membership object. The GetAllUsers() method returns all the users of the web site in the form MembershipUserCollection. The collection is then bound with the GridView as before. This overload of BindGrid() is called from the Load event of the user control and also from various other places.

Figure 2 shows the Page_Load event handler of the user control.

protected void Page_Load(object sender, 
EventArgs e)
{
CreateUserWizard1.ContinueDestinationPageUrl = 
Request.Path;
if (!IsPostBack)
   BindGrid();
}

Figure 2: Page_Load event handler of user control

The code sets the ContinueDestinationPageUrl property of the CreateUserWizard control to the URL of the current web form. This way the administrator is redirected to the same web form after clicking the Continue button of the CreateUserWizard control. The code also calls the BindGrid() method to bind the GridView with the list of users.

Handling Paging and Data Binding of the GridView

There might be many users registered with the web site and hence the GridView must implement paging functionality. Since we are not using any Data Source controls we need to implement paging functionality ourselves. In order to do so we must handle two events related to paging � PageIndexChanging and PageIndexChanged. The former is raised when a new page number is selected but before navigating to the new page. The later is raised when the page index has been changed. Figure 3 shows the event handlers for these two events.

protected void GridView1_PageIndexChanging
(object sender, GridViewPageEventArgs e)
{
GridView1.PageIndex = e.NewPageIndex;
BindGrid();
}
 
protected void GridView1_PageIndexChanged
(object sender, EventArgs e)
{
Label21.Text = "";
MultiView1.ActiveViewIndex = -1;}

Figure 3: Handling paging of the GridView

The PageIndexChanging event handler receives an event argument of type GridViewPageEventArgs. The GridViewPageEventArgs class contains a property called NewPageIndex that specifies the new page index. The code sets the PageIndex property of the GridView to the value indicated in the NewPageIndex property and calls the BindGrid() method.

In the PageIndexChanged event we clear off the MultiView by setting its ActiveViewIndex property to -1. This way no View control will be visible and we will be saved from the mismatch between currently displayed users and user details already shown. For the same reason we also set the title label to empty string.

When the administrator clicks on the Show button from a row we need to display the details about that user. That means the Click event handler of the Show button needs to know the UserName of the user that is shown by that row. In order to satisfy this requirement we handle RowDataBound event of the GridView. The RowDataBound event is raised for each and every row (including header and footer row) when the row is data bound. Figure 4 shows the RowDataBound event handler of the GridView.

protected void GridView1_RowDataBound
(object sender, GridViewRowEventArgs e)
{
if (e.Row.RowType == DataControlRowType.DataRow && 
e.Row.RowState == DataControlRowState.Normal)
{
Button b1 = (Button)e.Row.FindControl("Button1");
Label l = (Label)e.Row.FindControl("Label1");
b1.CommandName = e.Row.RowIndex.ToString();
b1.CommandArgument = l.Text;
}
}

Figure 4: Handling RowDataBound event of the GridView.

We need to execute our code only for the rows that contain data i.e. excluding header and footer rows and those that are in read only mode. This is done with the help of RowType and RowState properties as shown. The code then retrieves a reference to the Show button and user name label by calling FindControl() method. Then the CommandName property of the button is set to the current row index. Similarly the CommandArgument is set to the user name as displayed in the label. This way the Show button�s Click event handler will know which row has been clicked and what the corresponding user name is.

Editing and deleting users

Recollect that we have Edit, Delete, Update and Cancel buttons in the ItemTemplate and EditItemTemplate of the GridView template column. We have set the CommandName property of these buttons to Edit, Delete, Update and Cancel respectively. Due to this these buttons will raise RowEditing, RowDeleting, RowUpdating and RowCancelingEdit events when clicked.  We will handle these events in order to add editing capabilities to our GridView. Figure 5 shows the event handlers for these events.

protected void GridView1_RowEditing
(object sender, GridViewEditEventArgs e)
{
GridView1.EditIndex = e.NewEditIndex;
BindGrid();
}
 
protected void GridView1_RowUpdating
(object sender, GridViewUpdateEventArgs e)
{
Label l=(Label) GridView1.Rows[e.RowIndex].
FindControl("Label1");
TextBox t1 = (TextBox)GridView1.Rows[e.RowIndex].
FindControl("TextBox12");
TextBox t2 = (TextBox)GridView1.Rows[e.RowIndex].
FindControl("TextBox13");
MembershipUser user = Membership.GetUser(l.Text);
user.Email = t1.Text;
user.Comment = t2.Text;
Membership.UpdateUser(user);
GridView1.EditIndex = -1;
BindGrid();
}
 
protected void GridView1_RowCancelingEdit
(object sender, GridViewCancelEditEventArgs e)
{
GridView1.EditIndex = -1;
BindGrid();
}
 
protected void GridView1_RowDeleting
(object sender, GridViewDeleteEventArgs e)
{
Label l = (Label)GridView1.Rows[e.RowIndex].
FindControl("Label1");
Membership.DeleteUser(l.Text);
BindGrid();
}

Figure 5: Editing and deleting related event handlers

The RowEditing event handler receives a parameter of type GridViewEditEventArgs. The GridViewEditEventArgs class contains a property called NewEditIndex that indicates the row index of the row whose Edit button has been clicked. The code sets EditIndex property of the GridView to the value of the NewEditIndex property. Setting the EditIndex property will cause the GridView to enter in edit mode and EditItemTemplate will be displayed. The code then calls the BindGrid() method to bind the GridView again.

The RowUpdating event is the important event wherein the actual operation of updating the user is done. The RowUpdating event receives a parameter of type GridViewUpdateEventArgs. The GridViewUpdateEventArgs class provides the row index of the row being updated via RowIndex property. The code retrieves a reference to the row being updated from the Rows collection of the GridView. The code further obtains references to the user name label and email and comment textboxes using FindControl() method. The MembershipUser object corresponding to the specified user is obtained using the GetUser() method of the Membership object. The code then updates the Email and Comment properties of the MembershipUser instance and updates the user information back using UpdateUser() method of the Membership object. Finally, EditIndex property of the GridView is set to -1 to take the GridView back into read only mode.

The RowCancelingEdit event handler simply sets the EditIndex property of the GridView to -1 and binds the GridView again. This will take the GridView into read only mode.

The RowDeleting event handler receives a parameter of type GridViewDeleteEventArgs. The GridViewDeleteEventArgs class supplies the RowIndex of the row whose Delete button has been clicked. This RowIndex is used to retrieve the row being deleted from Rows collection of the GridView. Once the UserName is obtained from the user name label the code calls DeleteUser() method of the Membership object and rebinds the GridView.

Showing the user information

Each row of the GridView displays a Show button in addition to Edit and Delete buttons. The administrator can select the category of information to view and click on the Show button. Figure 6 shows the outline of code that goes in the Click event handler of the Show button.

protected void Button1_Click1
(object sender, EventArgs e)
{
Button b = (Button)sender;
MembershipUser user = Membership.
GetUser(b.CommandArgument.ToString());
ViewState["username"] = user.UserName;
Label21.Text = "Managing Details for " + user.UserName;
DropDownList ddl = (DropDownList)GridView1.
Rows[int.Parse(b.CommandName)].FindControl("DropDownList1");
switch (ddl.SelectedValue)
{
case "Status":
�
case "Activity":
�
case "Security":
�
case "Roles":
�
case "Profile":
�
}
}

Figure 6: Click event handler of Show button

The code first typecasts the sender parameter into a Button. This way we get reference to the Show button that is being clicked. Recollect that we have set the CommandArgument property of the Show button to the UserName. The code retrieves a MembershipUser instance by calling GetUser() method of the Membership object passing this CommandArgument as a parameter. The MembershipUser instance is used by the remaining code to extract required information about the user. The title label shows the UserName of the user whose details are being shown. The UserName is persisted in a ViewState variabled called username for later reference. Finally there is a switch statement that tests the selection in the DropDownList control. Each case of the switch statement populates and displays the corresponding View control from the MultiView control. The code of each case is discussed next.

Showing user status

The User Status panel shows whether the user is active, locked our and on line. The case for user status contains the code as shown in Figure 7.

...
case "Status":
CheckBox1.Checked = user.IsApproved;
CheckBox2.Checked = user.IsLockedOut;
CheckBox3.Checked = user.IsOnline;
if (user.IsLockedOut)
{
   CheckBox2.Enabled = true;
}
else
{
   CheckBox2.Enabled = false;
}
MultiView1.ActiveViewIndex = 0;
break;
...

Figure 7: Case for User Status

The code simply sets the checkbox values depending on the three Boolean properties of MembershipUser instance � IsApproved, IsLockedOut and IsOnline. The user might have registered with the web site but his account may not be activated. This is indicated by the IsApproved property. By default when the user performs five unsuccessful login attempts his account gets marked as locked. This is indicated by IsLockedOut property. The administrator can unlock a user only if his account is locked. Hence the lock out checkbox is enabled only if the account is locked out. Similarly the IsOnLine property indicates whether the user is currently logged in to the web site. The administrator can not change the on line status of the user and hence the related checkbox is always disabled. After assigning the Checked property the ActiveViewIndex property of the MultiView is set to 0. This causes the MultiView to show the User Status panel in the browser.

Showing user activity

The case for User Activity looks like Figure 8.

...
case "Activity":
Label11.Text = user.CreationDate.ToString();
Label12.Text = user.LastActivityDate.ToString();
Label13.Text = user.LastLockoutDate.ToString();
Label14.Text = user.LastLoginDate.ToString();
Label15.Text = user.LastPasswordChangedDate.ToString();
MultiView1.ActiveViewIndex = 1;
break;
...

Figure 8: Case for User Activity

The code in Figure 8 is fairly simple. It simply retrieves various activity related properties and sets the labels accordingly. All the activity related properties return DateTime instance. The CreationDate property returns the date and time at which the user registered with the web site. The LastActivityDate property indicates the date and time when the user was last authenticated. The LastLockoutDate property returns the date and time when the user account was last locked out. A user account can get locked out when the user tries to signing in unsuccessfully for the number of attempts as indicated by MaxInvalidPasswordAttempts property of the underlying provider. The LastLoginDate property returns the date and time at which the user last logged in to the web site. Finally LastPasswordChangedDate property returns the date and time when the user last changed the password. The ActiveViewIndex property of the MultiView control is set to 1 this time.

Managing security details

The case for Security is shown in Figure 9.

...
case "Security":
Label22.Text = user.PasswordQuestion;
if (!Membership.Provider.EnablePasswordReset)
{
Button5.Enabled = false;
}
if (!Membership.Provider.EnablePasswordRetrieval)
{
Button4.Enabled = false;
}
if (Membership.Provider.PasswordFormat == 
MembershipPasswordFormat.Hashed)
{
Button4.Enabled = false;
Button5.Enabled = false;
}
MultiView1.ActiveViewIndex = 2;
break;
...

Figure 9: Case for Security

The code displays the password question as entered by the user at the time of registration. Then the code checks if the password reset feature is enabled by the membership provider. This is done by checking the EnablePasswordReset property. Depending on the outcome of the check the Reset Password button is enabled or disabled. On the same lines the code checks if the password retrieval feature is enabled by the membership provider. This is done by checking the EnablePasswordRetrieval property. Additionally the code checks if the password storage format is Hashed. Hashed passwords can not be retrieved even if EnablePasswordRetrieval property returns true.  Accordingly the Get Password button is enabled or disabled. Finally ActiveViewIndex property of the MultiView control is set to 2 so that the Security View is shown.

The Security panel allows the administrator to perform in all four operations � changing the password, changing the security question and answer, retrieve password and reset password. The Click event handler for Change Password button is shown in Figure 10.

protected void Button6_Click
(object sender, EventArgs e)
{
MembershipUser user = Membership.
GetUser(ViewState["username"].ToString());
bool result = user.ChangePassword(
TextBox3.Text, TextBox4.Text);
}

Figure 10: Changing the password

The code retrieves the MembershipUser instance by calling GetUser() method of the Membership object. Recollect that the Click event of the Show button stores the user name in a ViewState variable called username. The same ViewState variable is used here and passed to the GetUser() method. Finally ChangePassword() method of the MembershipUser instance is called. The ChangePassword() method accepts the old and new passwords and returns the true or false depending on success or failure to change the password.

Figure 11 shows the Click event handler of the Change Password Q & A button.

protected void Button7_Click
(object sender, EventArgs e)
{
MembershipUser user = Membership.GetUser(
ViewState["username"].ToString());
bool result = user.ChangePasswordQuestionAndAnswer(
TextBox6.Text, TextBox7.Text, TextBox8.Text);
}

Figure 11: Changing the password question and answer.

The code retrieves the MembershipUser instance by calling GetUser() method of the Membership object. Then ChangePasswordQuestionAndAnswer() method of the MembershipUser instance is called. The ChangePasswordQuestionAndAnswer() method accepts three parameters � password, new question and new answer. The method returns true or false indicating the successor failure of the operation.

The Click event handler of the Get Password button is shown in Figure 12.

protected void Button4_Click
(object sender, EventArgs e)
{
MembershipUser user = Membership.GetUser(
ViewState["username"].ToString());
string str = user.GetPassword(TextBox9.Text);
Label30.Text = "Password :" + str;
}

Figure 12: Retrieving the password

The code retrieves the MembershipUser instance by calling GetUser() method of the Membership object. In order to retrieve password of the user GetPassword() method is called. The GetPassword() method accepts password answer as a parameter and returns the password. The password is then displayed in a label.

Finally the Click event handler of Reset Password button is shown in Figure 13.

protected void Button5_Click
(object sender, EventArgs e)
{
MembershipUser user = Membership.GetUser(
ViewState["username"].ToString());
string str = user.ResetPassword(TextBox9.Text);
Label30.Text = "Password has been reset to :" + str;
}

Figure 13: Resetting the password

The code retrieves the MembershipUser instance by calling GetUser() method of the Membership object as before. Then ResetPassword() method of the MembershipUser instance is called. The method accepts the password answer as a parameter and returns the new password as a return value. The new password is then displayed in a label.

Summary

In this part we covered display of user information and password management. The next part will deal with role management and managing profile of the user.

 




Bipin Joshi is a software consultant, an author and a yoga mentor having 21+ years of experience in software development. He conducts online courses in ASP.NET MVC / Core, jQuery, and Design Patterns. He is a published author and has authored or co-authored books for Apress and Wrox press. Having embraced Yoga way of life he also teaches Meditation to interested individuals. To know more about him click here.

Get connected : Twitter  Facebook  Google+  LinkedIn

Posted On : 09 Dec 2007



Tags : ASP.NET Web Forms Custom Controls Security Administration