FBA and User Display Names in SharePoint 2007

If you have been working with FBA for a while, you might have noticed that the user name displayed in the Welcome control shows the account name of the FBA user. This is because of the field the welcome control is mapped to.

image

As shown here, the welcome control simply points to the name property of the SPUser object.  One way of making it to display the full name is to import User Profile from your FBA data source and wait for the timer service job to complete to see the full name displayed in the Welcome control.  I've explained the other way below.

This was a very recent request where the user wanted to dynamically change the display name the first time a user logs into the FBA authentication enabled site.  It's very simple to achieve, but requires some careful consideration of things to get it to work perfectly!

In my below sample, I have configured FBA using LDAP provider.  This is kind of simple, because I can easily use DirectoryServices API to fetch user properties from AD.  I implemented this functionality in the form of a web part.  I also have a button and on clicking it I change the display name.  Here's the complete web part code:

 using System;
using System.Runtime.InteropServices;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Xml.Serialization;
using System.DirectoryServices;
using System.IO;

using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
using Microsoft.SharePoint.WebPartPages;

namespace FBANameChanger
{
    [Guid("8d557e7e-a7ef-45e7-a91c-386faf6c0389")]
    public class ChangeFbaName : System.Web.UI.WebControls.WebParts.WebPart
    {
        public ChangeFbaName()
        {
        }

        Button btn = null;

        protected override void CreateChildControls()
        {
            btn = new Button();
            btn.Text = "Click to change...";
            btn.Click +=new EventHandler(btn_Click);
            Controls.Add(btn);
        }

        public override void RenderControl(HtmlTextWriter writer)
        {
            writer.Write("<table><tr><td>");
            btn.RenderControl(writer);
            writer.Write("</td></tr></table>");
        }

        private static string ReturnADEmail(string accntName)
        {
            string email = string.Empty;
            DirectoryEntry de = null;
            DirectorySearcher ds = null;
            try
            {
                de = new DirectoryEntry("", "sr", "sr", AuthenticationTypes.Secure);
                ds = new DirectorySearcher(de);
                ds.Filter = string.Format("(sAMAccountName={0})", accntName);
                SearchResult sr = ds.FindOne();
                if (sr != null)
                {
                    de = sr.GetDirectoryEntry();
                    email = de.Properties["mail"].Value.ToString();
                }
                else
                {
                    // If nothing found handle that case here
                }
            }
            catch (Exception e)
            {
                // Exception handling mechanism
            }
            finally
            {
                if (ds != null)
                    ds.Dispose();
                if (de != null)
                    de.Dispose();
            }
            return email;
        }

        private static string ReturnADDisplayName(string accntName)
        {
            string fullName = string.Empty;
            DirectoryEntry de = null;
            DirectorySearcher ds = null;
            try
            {
                de = new DirectoryEntry("", "sr", "sr", AuthenticationTypes.Secure);
                ds = new DirectorySearcher(de);
                ds.Filter = string.Format("(sAMAccountName={0})", accntName);
                SearchResult sr = ds.FindOne();
                if (sr != null)
                {
                    de = sr.GetDirectoryEntry();
                    fullName = de.Properties["cn"].Value.ToString();
                }
                else
                {
                    // If nothing found handle that case here
                }
            }
            catch (Exception e)
            {
                // Exception handling mechanism
            }
            finally
            {
                if (ds != null)
                    ds.Dispose();
                if (de != null)
                    de.Dispose();
            }
            return fullName;
        }        

        private void btn_Click(object sender, EventArgs e)
        {
            SPSecurity.RunWithElevatedPrivileges(delegate()
            {
                string identity = Page.User.Identity.Name;
                string loginName = "mossdcLdapMembers:" + identity;
                string displayName = ReturnADDisplayName(identity);
                string email = ReturnADEmail(identity);
                SPWeb web = SPContext.Current.Web;
                web.AllowUnsafeUpdates = true;
                try
                {
                    SPUser user = web.AllUsers[loginName];
                    user.Name = displayName;
                    user.Email = email;
                    user.Update();
                    Page.Response.Redirect(web.Site.Url.ToString());
                }
                catch (UnauthorizedAccessException _unauthorized)
                { Page.Response.Redirect(web.Site.Url.ToString()); }
                catch (Exception _e)
                { }
            });
        }
    }
}

Most of the code snippet is self-explanatory.  However, I want to highlight on some parts where you might ask "Why?".

                 try
                {
                    SPUser user = web.AllUsers[loginName];
                    user.Name = displayName;
                    user.Email = email;
                    user.Update();
                    Page.Response.Redirect(web.Site.Url.ToString());
                }
                catch (UnauthorizedAccessException _unauthorized)
                { Page.Response.Redirect(web.Site.Url.ToString()); }

In the above code snippet, I've used explicit redirection in the "try" block because it requires a refresh to show the modified user full name.  Instead of using VB/Java Script functions, I simply redirect the control to the home page.  The "catch" block, catches the "UnauthorizedAccessException".  I have noticed that whenever the user context has read only permissions on the site, the Update method call on SPUser object throws an "Access Denied" exception.  I tried a lot to debug this to see why this happens, but couldn't.  So, as a work-around I simply redirect.

Strangely, even though the user.Update() call in the above code throws an unauthorized exception, the call actually succeeds.

Below screenshot shows the deployed web part I used.

image

Once I click the "Click to change..." button, I see the modified full name of the current user that's logged in.  A screenshot of that below:

image

If you are using your own repository for user account (like a database etc.,).  You'll simply need to modify the logic to retrieve the respective values back from your repository.  In the above code, I've simply modified two properties, the display name and the email address.  You can modify other properties in a similar manner.

A more comprehensive approach could be to write a class library that implements methods to return the properties you need and using it in your web part.

I hope this post was helpful!