Sunday, May 23, 2010

Pre-populating the Login Control’s Username and Password

We’ve all visited websites that provide a test or demo site and allow you to login for the purpose of testing features out using specific user accounts with assigned roles. If you have a website or web application that needs to provide this type of functionality, I’m going to show you how you can save the user a few keystrokes by filling in the Username and Password for them, so that all they have to do is click the Login button.

What I have done is created three accounts: Admin, User, and Guest along with a Default.aspx page which resides under the respective folder. The folder structure is as follows for these accounts:

Admin/Default.aspx

User/Default.aspx

Guest/Default.aspx

I’ve created a login page (Login.aspx) and dropped a Login Control on it. The Login Control has been converted to a template and I’ve added a label and dropdown list to it. The dropdown list contains the user accounts that will be selected for logging in.

image

Within the Load event of the Login Control I get a reference to the dropdown list and password textbox controls, so that they can be used in selecting the proper user and populating the password textbox. I add an attribute to the password textbox, which sets the password, so that the user does not have to type it in. Something to be mindful of is that the password is viewable in plain text if you view the pages source. Because these accounts are being used to save the user a few of keystrokes for a test or demo site login this isn’t much of an issue.

DropDownList userLogin = (DropDownList)DemoLogin.FindControl("UserDropDownList");
TextBox passwd = (TextBox)DemoLogin.FindControl("Password");
switch (userLogin.SelectedValue)
{
   case "Admin":
       DemoLogin.UserName = userLogin.SelectedValue;
       passwd.Attributes.Add("value", "Adm!n123");
       break;
   case "User":
       DemoLogin.UserName = userLogin.SelectedValue;
       passwd.Attributes.Add("value", "U$er123");
       break;
   case "Guest":
       DemoLogin.UserName = userLogin.SelectedValue;
       passwd.Attributes.Add("value", "Gue$t123");
       break;
   default:
       DemoLogin.UserName = string.Empty;
       passwd.Attributes.Add("value", "");
       break;
}


The only other thing needed is within the Login Control’s LoggedIn event to check the role of the user and then redirect them to the appropriate Default.aspx page.



if (Roles.IsUserInRole(DemoLogin.UserName, "Admin"))
{
   Response.Redirect("~/Admin/Default.aspx");
}
else if (Roles.IsUserInRole(DemoLogin.UserName, "User"))
{
   Response.Redirect("~/User/Default.aspx");
}
else if (Roles.IsUserInRole(DemoLogin.UserName, "Guest"))
{
   Response.Redirect("~/Guest/Default.aspx");
}
else
{
   Response.Redirect("~/Login.aspx");
}


image 



While this is pretty trivial, it does save a few keystrokes by allowing the user to just select the user account they wish to login as and then all they have to do is click the login button.



PopulateLoginSample.zip

Thursday, May 6, 2010

Display a list of locked users

I’ve created a simple example that both shows how you can display to the user the number of failed login attempts and disable their login after the specified max number of attempts you define. I’ve also created a simplified admin screen that displays all users that currently are locked out and allows you to select and unlock them after logging in as a user with the admin role.
This example uses a Master Page and within the code-behind is where you keep track of the number of failed login attempts. I’ve set the maxInvalidPasswordAttempts = 3 within the web.config as this is where the hard limit is defined.
<add name="AspNetSqlMembershipProvider" connectionStringName="MyAspNetDB" applicationName="/" 
passwordFormat="Hashed"
passwordAttemptWindow="30"
minRequiredNonalphanumericCharacters="1"
minRequiredPasswordLength="7"
maxInvalidPasswordAttempts="3" 
type="System.Web.Security.SqlMembershipProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"/>


Back in the Master Page’s code-behind I’ve defined a member variable called failedLoginCount that will contain the number of failed logins. In addition I’ve added a Page_Init that stores the failedLoginCount in ViewState. I choose to use ViewState because this is a very small amount of information that will be stored, so it won’t inflate the ViewState tremendously.


public int failedLoginCount;

protected void Page_Init(object sender, EventArgs e)
{
ViewState.Add("FailedLoginCount", failedLoginCount + 1);
}

protected void Page_Load(object sender, EventArgs e)
{
failedLoginCount = (int) ViewState["FailedLoginCount"];
}


Now within the Login Control’s LoggedIn event the role the user is a member of is checked and if they are in the Administrator role they are redirected to the Admin page; otherwise they are assumed to be a standard user and directed to the Standard users page.


protected void Login1_LoggedIn(object sender, EventArgs e)
{
Login logIn = (Login) LoginView1.FindControl("Login1");
if (Roles.IsUserInRole(logIn.UserName, "Administrators"))
{
Response.Redirect("~/Admin/Admin.aspx");
}
else
{
Response.Redirect("~/Standard/Standard.aspx");
}
}


It is within the Login Control’s LoginError event that failedLoginCount is checked to see if it equals the maxInvalidPasswordAttempts value that is defined within the web.config file. I get a reference to the Login control that is contained within a LoginView Control and then set the FailureText letting the user know that their account has been disabled and they need to contact the administrator once their failedLoginCount equals 3. This is followed by locking the user’s account, more formally setting IsApproved to false, which indicates that the user cannot be authenticated. If the failedLoginCount is not equal to 3 the failedLoginCount gets incremented and added to the ViewState key defined as well as the current count is displayed to the user.


protected void Login1_LoginError(object sender, EventArgs e)
{
string emailLink = "<a href=mailto:admin@somesite.com>";
emailLink += "Site Administrator";
emailLink += "</a>";

Login logIn = (Login)LoginView1.FindControl("Login1");

if (failedLoginCount == 3)
{
logIn.FailureText = "Your account has been disabled. Please contact the site administrator: " + emailLink;

MembershipUser user = Membership.GetUser(logIn.UserName);
user.IsApproved = false;
}
else
{
logIn.FailureText = "Failed login attempt [" + ViewState["FailedLoginCount"].ToString() + "]";
failedLoginCount++;
ViewState.Add("FailedLoginCount", failedLoginCount);
}
}


image


image


image


Now for the Admin portion I simply have a GridView Control that displays the list of users on the site whose accounts are currently locked out. A simple SQL query provides the list of users. I’ve coded this in-line, but in a real application I highly suggest that you put this into a Stored Procedure.


SELECT UserName, Email, LastLockoutDate, IsLockedOut 
FROM vw_aspnet_MembershipUsers 
WHERE IsLockedOut = 1


The unlocking of users takes place within the RowUpdating event of the GridView by calling UnlockUser() on the reference to MembershipUser. The following is the code for the admin section.


protected void Page_Load(object sender, EventArgs e)
{
if (!Page.IsPostBack)
{
LockedUsersGridView.DataSource = GetLockedUsers();
LockedUsersGridView.DataBind();
}
}

private DataTable GetLockedUsers()
{
DataTable dt = new DataTable();
SqlDataAdapter da = new SqlDataAdapter();

using (SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["MyAspNetDB"].ConnectionString))
{
using (SqlCommand cmd = new SqlCommand("SELECT UserName, Email, LastLockoutDate, IsLockedOut FROM vw_aspnet_MembershipUsers WHERE IsLockedOut = 1", conn))
{
cmd.CommandType = CommandType.Text;

try
{
conn.Open();
da.SelectCommand = cmd;
da.Fill(dt);

if (dt.Rows == null || dt.Rows.Count < 1)
{
LockedUsersLabel.Text = string.Empty;
return dt;
}

cmd.ExecuteNonQuery();
}
catch (SqlException sqlEx)
{
LockedUsersGridView.Rows[0].Cells[0].Text = sqlEx.ToString();
}
}
}

return dt;
}

protected void LockedUsersGridView_SelectedIndexChanging(object sender, GridViewSelectEventArgs e)
{
LockedUsersGridView.PageIndex = e.NewSelectedIndex;
LockedUsersGridView.DataSource = GetLockedUsers();
LockedUsersGridView.DataBind();
}

protected void LockedUsersGridView_RowEditing(object sender, GridViewEditEventArgs e)
{
LockedUsersGridView.EditIndex = e.NewEditIndex;
LockedUsersGridView.DataSource = GetLockedUsers();
LockedUsersGridView.DataBind();
}

protected void LockedUsersGridView_RowCancelingEdit(object sender, GridViewCancelEditEventArgs e)
{
LockedUsersGridView.EditIndex = -1;
LockedUsersGridView.DataSource = GetLockedUsers();
LockedUsersGridView.DataBind();
}

protected void LockedUsersGridView_RowUpdating(object sender, GridViewUpdateEventArgs e)
{
TextBox userName = LockedUsersGridView.Rows[e.RowIndex].FindControl("UserNameTextBox") as TextBox;

if (userName != null)
{
MembershipUser user = Membership.GetUser(userName.Text.Trim());
user.UnlockUser();
Membership.UpdateUser(user);
}

LockedUsersGridView.EditIndex = -1;
LockedUsersGridView.DataSource = GetLockedUsers();
LockedUsersGridView.DataBind();
}

protected void LockedUsersGridView_PageIndexChanging(object sender, GridViewPageEventArgs e)
{
LockedUsersGridView.PageIndex = e.NewPageIndex;
LockedUsersGridView.DataSource = GetLockedUsers();
LockedUsersGridView.DataBind();
}


Hopefully you’ll find this useful as a basis for creating a more advanced admin screen for user management.


image


image


image


image


image


image

DisplayLockedUsers.zip