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

No comments: