RSS
Add to Technorati Favorites
Follow me on Twitter


Notifying Users Why Their Login Failed in the ASP.NET Login Control
As a follow-on to my last post on using FBA sites in SharePoint, I wanted to point out an additional enhancement you can make, which will help out your users.
 
When a user tries to log into your Web site using the Login control, unfortunately, it often just tells the user a bland message such as "invalid login". That doesn't give the user enough information. Is their username wrong? Is the password wrong? Are the username and password correct but the user's been locked out? Users have no idea what the true problem is. I found a way around this by manually validating the user, discovering any error messages, and sending those to the user, and I'd like to show you how to do this as well.
 
The most difficult part of this is that if a user is using the wrong password, your Membership Provider will increment a field called FailedPasswordAttemptCount in the Membership table in your database. Once the user has reached the maximum number of failed attempts (as specified by the MaximumInvalidPasswordAttempts attribute of your provider node), the user's account will be locked.
 
If you use a method such as Membership.ValidateUser(), that field will be queried in the database. Unfortunately, though, I don't know of a good way to access that value directly from the MembershipUser object, the way you can find out things like MembershipUser.IsLockedOut.
 
To get around this, I simply created a new stored procedure that I added to my membership database. (I'm not using multiple application names in my web application, so I'm doing a "poor man's" query of simply retrieving a user by the user name they typed into the login box.) Here's the stored procedure:
 
CREATE PROCEDURE [dbo].[aspnet_Membership_GetFailedPasswordAttemptCount]
    @UserName  nvarchar(256)
AS
 DECLARE @UserId  uniqueidentifier
 DECLARE @FailedPasswordAttemptCount  int
 
 SELECT  @UserId = u.UserId
 FROM    dbo.aspnet_Users u
 WHERE   @UserName = u.LoweredUserName
   
 SELECT FailedPasswordAttemptCount
 FROM aspnet_Membership
 WHERE UserId = @UserId
 
Then, I simply added a method that executes this stored procedure:
 
private int GetFailedPasswordAttemptCount(string userName)
{
    SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["FBAConnectionString"].ConnectionString);
    SqlCommand cmd = new SqlCommand("aspnet_Membership_GetFailedPasswordAttemptCount", conn);
    cmd.CommandType = System.Data.CommandType.StoredProcedure;
    cmd.Parameters.Add("@Username", System.Data.SqlDbType.VarChar, 255).Value = userName;
    int failedAttempts = 0;
    try
    {
        conn.Open();
        failedAttempts = (int)cmd.ExecuteScalar();
        conn.Close();
    }
    catch
    {
        throw;
    }
    finally
    {
        if (conn.State != System.Data.ConnectionState.Closed)
        {
            conn.Close();
        }
    }
    return failedAttempts;
}

 
Finally, I create a custom event handler for the Authenticate event on the ASP.NET Login control I'm using.
 
public void loginCtrl_Authenticate(object sender, AuthenticateEventArgs e)
{
    //Manually validate the user. If they're valid, then they authenticate just fine and get logged in.
    bool isValid = Membership.ValidateUser(loginCtrl.UserName, loginCtrl.Password);
    if (isValid)
    {
        e.Authenticated = true;
    }
    else
    {
        MembershipUser user = Membership.GetUser(loginCtrl.UserName);
        //First, check to see if the username was found in the Membership database
        if (user != null)
        {
            //Next, find out if the user has been locked out.
            if (user.IsLockedOut)
            {
                //If the user's account is locked out, notify them.
                e.Authenticated = false;
                loginCtrl.FailureText = "Your account is locked out. Please contact an administrator.";
            }
            //If the user isn't locked out, it means their password is wrong. Notify them how many times they have entered a false password, and how many times they can have a failed login before they're locked out.
            else
            {
                e.Authenticated = false;
                int failedAttempts = GetFailedPasswordAttemptCount(user.UserName);
                string errorMsg = "Your password is incorrect. Your login has failed {0} times. After {1} failed logins you will be locked out.";
                loginCtrl.FailureText = String.Format(errorMsg, failedAttempts, Membership.MaxInvalidPasswordAttempts);
            }
        }
        else
        {
            e.Authenticated = false;
            loginCtrl.FailureText = "The username cannot be found.";  
        }
    }
}

 
Finally, simple register the event handler with the Login control's Authenticate event:
 
loginCntrl.Authenticate += new AuthenticateEventHandler(loginCtrl_Authenticate);
 
In my case, my Login control is hosted in a Web Part. Even if your control is in an ASP.NET login page you've created, you'll simply add the last several methods to whatever control or page is hosting your Login control.
The 7 Habits: Highlights from Habits 1 - 3
Although I'm the type of person that is skeptical of "self-help" type literature, The 7 Habits of Highly Effective PeopleI heard one executive of a large company say that if you could only read one book, it should be this one. Although I've heard of this book for years, such a stirring review intrigued me. In addition, while I worked at Microsoft, I was able to watch a live webinar where I got to hear Stephen Covey himself present, so I thought I'd give it a shot.
 
After having read just the first half of the book, I've already found myself positively impacted by the simple, yet profound, ideas laid out by Mr. Covey. I'd like to share several of the points which have stood out to me so far. And if you find yourself wanting to read the book yourself, please do so; it's worth the time it takes to read it, in my opinion. The book isn't just about work, but about "life management".
 
Habit 1: Be Proactive
The key here is to be proactive, not reactive. (This something I really struggle with!) The reactive person only takes action when they are responding to someone else. What that means is that the reactive person is allowing their own behavior to be dictated by others. Instead, a mature person decides that they have the power to choose how they will respond to a situation or a person.
 
I have to admit, this is something that I really struggle with. I can get very exasperated at some clients who seem to want something for nothing, or co-workers who don't understand that I don't want to be called five times a day. I often let my frustration with these people consume me so much that I get distracted from my true objectives. It's true that I'm human and I'm going to have emotional responses to things, but they key is to move on and not let that sidetrack me.
 
Habit 2: Begin with the End in Mind
It's so easy to get stuck in a rut, to live every day as "Wake up, go to work, go to bed, repeat..." The second habit involves stepping back and looking at the big picture. What is really important in life? What drives you? If you allow yourself to be motivated by money, by being too codependent on another person, by what others think of you, by being identified with an organization, then you are always going to be at the whim of some outside force; to combat that, you will eventually cling tighter and tighter to that which you bind your identity to.
 
The author points out that if your life is grounded in what he calls "values", you will not be shaken by any of these. If you will indulge a little side note here, as a Christian, I believe that my life has intrinsic value because I was created in God's image; it doesn't matter if other people think I'm of value, I know that I have value because I was assigned it apart from any worth that I could achieve for myself. Furthermore, I believe that God has laid out for us how we are to live, and if we choose to live in obedience to Him, following the path of good and not bad, then we will avoid many negative consequences and will live a fuller life than if we consistently choose to engage in evil and destructive thoughts and activities. I believe that God desires to be the first thing in our life, and as long as we find our worth in Him and care more about what he thinks about us than what , we will be unmoved by what others say about us or what we think about ourselves. This will free us up to live proactive and not reactive lives.
 
Beginning with the end in mind means determining what the good and healthy things that you would like to achieve in your life. Setting these things out will help you gain a bigger perspective about what's important in your life. For instance, I say that I want to be healthy, but I have to admit... every single day, when it approaches 5 o'clock and I know I need to go to the gym, I say to myself, "But I'm really in the zone with my coding, and if I walk away now, I'll lose that momentum. If I don't go to the gym today, it won't kill me." The ironic thing, though, is that if I take that attitude my whole life and live an unhealthy live, it will kill me. As Mr. Covey points out, saying "yes" to something always means saying "no" to something else. After reading this book, I'm starting to realize that saying "yes" to more work means saying "no" to my health, and that's not acceptable to me.
 
Habit 3: Put First Things First
Maybe one of the most famous ideas from this book is the idea of the 4 quadrants; imagine a grid with 2 rows and 2 columns; on one side you see the headings "important" and "unimportant", and "urgent" and "not urgent". Most people who allow others to drive their activities (those who are reactive) live in the "important and urgent" box. People who are constantly "putting out fires" are stressed and get burned out quickly. These kind of people try to escape that mentality by engaging in "not urgent" and "unimportant" activities, which is not particularly useful, either. People who are proactive will pull them away from some of the most urgent activities of the day in order spend time on "important" but "not urgent" activities. To put it in other words, it's important to distance yourself enough from the "tactical" battles of everyday life to find time to act "strategically".
 
Because I work for myself, I sometimes worry that I won't have enough work to live off of. This not only means that I tend to take on too much work at any given time, it also means I tend to say "yes" to too much work because I'm afraid if I tell someone "no", it doesn't just mean "no for now", but I have this irrational fear that they'll hang up the phone and never want to do work with me again. What this means is that I'm continually overpromising work and then constantly trying to peddle faster to meet all the timelines I've allowed to be defined for myself.  After reading this book, I realized that I needed time to "get off the treadmill" so to speak. This has meant a couple things for me:
  • The fact that I'm even taking a half hour each day to read this book, in the hopes that it will make me a more effective person at work, even though it's 'non-billable' work, is already reducing stress in life. I've made a personal choice to become a better person rather than allow looming timelines to always dictate how I spend my time.
  • I'm saying "no" to more work, even if I think, "I could probably squeeze some time in for that." Taking time off to take a few breaths and remember that saying "no" doesn't mean the end of a relationship means I think before I commit.
  • I had a situation this week where I was frustrated at the actions of an individual with whom I've worked with for a year and a half. I was so upset I was ready to walk away from the relationship. Although I knew I had work that needed to get done, I chose instead to drive across town to have lunch and work out the matter. (I might add that I felt I had the freedom to do this because I've started saying "no" to too much work... see previous bullet.) The relationship was repaired and hopefully now I have maintained the ability to continue to gain work from this person for the long term, which I achieved by sacrificing the "urgent" and "important" work of immediate deadlines.

All this is not to say that there aren't trade-offs; I can't just pretend like deadlines don't exist. If I take an action, I need to be prepared for the accompanying consequence. However, taking the time to focus on "preventative" measures can provide a better quality of life down the road.

My Sites without My Sites
Being able to import user profile information from a third party source like Active Directory is usually one of the most critical parts of a functioning Intranet site. What makes this even more powerful is when you layer on the ability for users to edit their own profile information, as well as search for other users. Layer on top of that the social networking features that My Sites provide, and you've got killer functionality.
 
The problem, though, is that some companies want this functionality without actually using My Sites. (This can be for various reasons, usually revolving around governance; an organization might not want employees "wasting time" by posting pictures of their poodle Fluffy, or they might fear that someone will start publishing official content on their private site.)
 
Well, with SharePoint 2010, this is now possible. You can use the Profile Import Service Application to import user data, and you can allow users to take advantage of People Search and social networking. However, you can do all this while preventing them from being able to actually create My Sites.
 
To easily accomplish this, go to your User Profile Service Application management page. Under the heading "People", click on the link that says "Manage User Permissions". This will open the following dialog:
 
User Profile Import Service - Manage User Permissions
 
For a list of what each of these permissions entails, visit the TechNet article: Enable or disable personal and social features for users or groups (SharePoint Server 2010).
 
To turn off the ability for all users to create a My Site, create a permission set for "All Authenticated Users", and uncheck the "Create Personal Site" checkbox, then save.
 
If users could create their own My Site, they would see a link in the My Site header that says "My Content", like this:
 
My Site links
 
However, if a user without a personal site select "My Site" from their user drop down menu, that link will not be available for them:
 
My Site without My Content
 
Users will be able to take advantage of things like editing their profile, tagging content, viewing their org chart, tracking colleagues, etc., but they won't be able to create their own My Site.
 
Please note, certain administrative users might still have the ability to create My Sites, even after you've disabled Personal sites for users. To completely eliminate the possibility of anyone creating a Personal site, turn off Self Service Site Creation in Central Administration. To do this, open up CA, then navigate to your Web Application. Click on the "Self-Service Site Creation" button on the ribbon, in the "Security" section. This will open up a dialog where you can turn of the ability for users to create a new site in that web application.
 
Self Service Site Creation dialog
 
Once you turn that off, if a user tried to create their own Personal site, they would get an error saying, "Your personal site cannot be created because Self-Service Site Creation is not enabled. Contact your site administrator for more information."
Central Administration Shortcut List
Something I've gotten frustrated with at times is that the new Central Administration landing page seems to be missing a lot of the links that I use on a regular basis. For instance, under the "Application Management" heading, there's a link to create a new Site Collection, but not delete one. (Often times, if I'm testing out the deployment of my solutions, I need to wipe away the Site Collection and create another one from scratch.) It's annoying to have to click on the link to the Application Management page.
 
Well, as someone who can be woefully ignorant of my surroundings, I just noticed today that they have provided a handy Web Part on the right side of the Central Administration homepage where you can add your own list of links to your most frequently used pages. How helpful! For those of you who also have missed this great little help, I thought I'd pass it along.
 
Central Administration homepage
SharePoint 2010 Tools in Visual Studio 2010 Slide Deck
Thanks to all who showed up today for my presentation at the St. Louis SharePoint User Group. For those interested, you can download my slide deck here.
 
1 - 5 Next