How can we add a lockout notification?

Oct 25, 2011 at 4:33 PM

I was wondering if anyone had a simple solution on how to add a lockout notification to a sign-in form based on this solution.  This should be displayed once the user tries to login incorrectly after the number of max unsuccessful attempt set in the web config file.  Any ideas, samples, links would be appreciated.

Coordinator
Oct 25, 2011 at 4:41 PM

I've customized a login page for a client to do just that.  I just updated the sign in control's loginerror event to set the error to "locked out" if the user was locked out:

        void signInControl_LoginError(object sender, EventArgs e)
        {
            System.Web.UI.WebControls.Login control = (System.Web.UI.WebControls.Login)sender;


            //Replace the error message if the user is locked out
            MembershipUser usrInfo = Visigo.Sharepoint.FormsBasedAuthentication.Utils.GetUser(control.UserName);
            if (usrInfo != null)
            {
                // Is this user locked out?
                if (usrInfo.IsLockedOut)
                {
                    control.FailureText = "Locked Out";
                }
            }


        }

Oct 25, 2011 at 8:20 PM

Hey Chris,

Thanks for the code sample.  Would you mind adding your sign-in page to the solution or sending it to me direct.  I've created one in a separate solution and the I'm interested in how you integrated yours with the FBA Pack.

Cheers,

Phil Marcuson

Coordinator
Oct 25, 2011 at 8:49 PM

I can't give you the full login page, as it is custom for a client and has lots of extra logic specifically for the client.  I also don't actually use the FBA Pack with it, but code very similar. Here it is, with some items removed or renamed, so it may not compile as is, but it is very close to the actual page so it shouldn't take many changes to get it working:

login.aspx:

 

<%@ Assembly Name="$SharePoint.Project.AssemblyFullName$" %>
<%@ Assembly Name="Microsoft.SharePoint.IdentityModel, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Assembly Name="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"%> 
<%@ Page Language="C#" CodeBehind="Login.aspx.cs" Inherits="mycompany.Login" MasterPageFile="~/_layouts/mycompany/Simple.master"       %> 
<%@ Import Namespace="Microsoft.SharePoint.WebControls" %> 
<%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %> 
<%@ Register Tagprefix="Utilities" Namespace="Microsoft.SharePoint.Utilities" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %> 
<%@ Import Namespace="Microsoft.SharePoint" %> 
<%@ Assembly Name="Microsoft.Web.CommandUI, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<asp:Content ID="Content1" ContentPlaceHolderId="PlaceHolderPageTitle" runat="server">
	<SharePoint:EncodedLiteral runat="server"  EncodeMethod="HtmlEncode" Id="ClaimsFormsPageTitle" />
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderId="PlaceHolderPageTitleInTitleArea" runat="server">
	<SharePoint:EncodedLiteral runat="server"  EncodeMethod="HtmlEncode" Id="ClaimsFormsPageTitleInTitleArea" />
</asp:Content>
<asp:Content ID="Content3" ContentPlaceHolderId="PlaceHolderSiteName" runat="server"/>
<asp:Content ID="Content4" ContentPlaceHolderId="PlaceHolderMain" runat="server">
 <div id="SslWarning" style="color:red;display:none">
 <SharePoint:EncodedLiteral runat="server"  EncodeMethod="HtmlEncode" Id="ClaimsFormsPageMessage" />
 </div>
  
 <asp:login id="signInControl" runat="server" width="100%">
	<layouttemplate>
		<asp:literal id="FailureText" runat="server"/>
		<table width="100%" class="profile-item-table">
		<tr>
			<th nowrap="nowrap"><SharePoint:EncodedLiteral ID="EncodedLiteral1" runat="server" text="<%$Resources:MyResources,Email%>" EncodeMethod='HtmlEncode'/></td>
			<td width="100%"><asp:textbox id="UserName" autocomplete="off" runat="server" class="ms-inputuserfield" width="99%" /></td>
		</tr>
		<tr>
			<th nowrap="nowrap"><SharePoint:EncodedLiteral ID="EncodedLiteral2" runat="server" text="<%$Resources:MyResources,Password%>" EncodeMethod='HtmlEncode'/></td>
			<td width="100%"><asp:textbox id="password" TextMode="Password" autocomplete="off" runat="server" class="ms-inputuserfield" width="99%"/></td>
		</tr>
        </table>
		<div class="mybuttoncontainer"><asp:button id="login" commandname="Login" text="<%$Resources:wss,login_pagetitle%>" runat="server" /></div><br /><br />
		<asp:checkbox id="RememberMe" text="<%$SPHtmlEncodedResources:wss,login_pageRememberMe%>" runat="server" /><br />
	</layouttemplate>
 </asp:login>
 <br />
 <asp:Literal ID="ForgotYourPassword" runat="server" />
</asp:Content>

 

login.aspx.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Web.Security;
using System.Web.UI.WebControls;
using Visigo.Sharepoint.FormsBasedAuthentication;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Utilities;

namespace mycompany
{
    public partial class Login : Microsoft.SharePoint.IdentityModel.Pages.FormsSignInPage
    {
        protected string Email { get; set; }

        protected string Username { get; set; }

        protected Literal ForgotYourPassword;

        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);

            
            //Have to set resource lookup here, so it happens after sharepoint sets the thread culture
            //from the requested web
            this.signInControl.FailureText = System.Web.HttpContext.GetGlobalResourceObject("MyResources", "LoginFailure").ToString();
            this.ForgotYourPassword.Text = @"<a href=""" + passwordRecoveryURL + @""">" + System.Web.HttpContext.GetGlobalResourceObject("MyResources", "ForgotYourPassword").ToString() + @"</a>";
            this.signInControl.LoggingIn += new LoginCancelEventHandler(signInControl_LoggingIn);
            this.signInControl.LoginError += new EventHandler(signInControl_LoginError);
        }

        void signInControl_LoginError(object sender, EventArgs e)
        {
            System.Web.UI.WebControls.Login control = (System.Web.UI.WebControls.Login)sender;


            //Replace the error message if the user is locked out
            MembershipUser usrInfo = Visigo.Sharepoint.FormsBasedAuthentication.Utils.GetUser(control.UserName);
            if (usrInfo != null)
            {
                // Is this user locked out?
                if (usrInfo.IsLockedOut)
                {
                    control.FailureText = System.Web.HttpContext.GetGlobalResourceObject("MyResources", "LockedOut").ToString();
                }
            }

            //Return username back to email address if there's an error
            control.UserName = Email;


        }

        void signInControl_LoggingIn(object sender, LoginCancelEventArgs e)
        {
            //Set username when an email address is entered
            Email = ((System.Web.UI.WebControls.Login)sender).UserName;
            Username = Utils.ConvertEmailToUsername(Email);

            if (Username != null)
            {
                ((System.Web.UI.WebControls.Login)sender).UserName = Username;

            }
    
        }

        
    }
}

Oct 25, 2011 at 9:27 PM

This is great!  Funny, it looks a little like what I have already :-), but the extra bits of code are very helpful.  I appreciate it.

Phil Marcuson

Jan 16, 2012 at 9:40 AM

I'm trying to implement this (different error message when user account is locked out) but I'm getting following error:

 'Visigo.Sharepoint.FormsBasedAuthentication.Utils' is inaccessible due to its protection level

Am I missing something?

 

Jan 17, 2012 at 3:48 AM

Instead of Utils.GetUser() I'm using Membership.GetUser() now. But I'm getting "Object reference not set to an instance of an object." error in base.OnLoad(e); where base is Microsoft.SharePoint.IdentityModel.Pages.FormsSignInPage. Any idea why will it be?

Coordinator
Jan 17, 2012 at 4:12 AM

The reason for the protection level error is because the Utils class in the FBA Pack is not declared public. As I mentioned earlier, the sample code was not actually against the FBA pack, so it may require some slight modifications. In fact, GetUser doesn't even exist in the current version of the Utils class.

I'd take a look at UserEdit.aspx.cs. I call the membership provider's GetUser method in there. So to get it to work, you could either change the FBA Pack's accessibility level on the Utils class and recompile, and add the code from UserEdit.aspx.cs. Or you could just copy the required code from the FBA Pack and paste it into your project.

I'm not sure why you're getting an object reference not set' - the only reason I can think of is that the membership provider isn't set up on the Web Application you've deployed to. Either way, I don't think it will work, because I don't believe SharePoint's membership provider has implemented the method.

Jan 17, 2012 at 6:09 AM
Edited Jan 17, 2012 at 6:11 AM

I do have setup FBA for the web app where I'm trying to customize the login page.

As long as I'm using

<%@ Page Title="Login" Language="C#" MasterPageFile="MyClient.master" Inherits="Microsoft.SharePoint.IdentityModel.Pages.FormsSignInPage" %>

I'm able to login.

But as soon as I use:

<%@ Page Title="Login" Language="C#" MasterPageFile="MyClient.master" AutoEventWireup="true" CodeFile="Login3.aspx.cs" Inherits="Login3" %>

I'm getting object reference not set error (even when my code behind file is completely empty)

Coordinator
Jan 17, 2012 at 12:03 PM

I see. I thought the object reference error was from the GetUserline. You'll get that if you don't already have the codebehind file also defined.  It's also CodeBehind, not CodeFile.