LsaLookupSids

One of my coworkers got married and changed her surname. After my colleague changed her domain user strange thing happened. We are using domain authentication on our web portal. After domain user change she didn’t have any rights on the portal anymore. I checked the log (I am using Elmah) and there it was: Validation failed for one or more entities. The strangest thing was that under User column there was old surname, not the new one! Firstly I checked AD. Everything was fine. Then I tried to restart web page and IIS – it didn’t help. Server reboot was out of option.

After that I asked uncle Google, if he knows anything about that stuff. And he directed me to stackoverflow article. To work around this issue you can do following:

  1. Open registry editor as admin (on Windows Server 2008 and newer you can find it via searchbox (type regedit) on older versions you can run it via Run).
  2. Locate following subkey: HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Lsa
  3. Right-click on it, point to New and click DWORD Value.
  4. Type in LsaLookupCacheMaxSize and press Enter.
  5. Set value to 0 and exit registry editor.

Warning: this registry entry disables local SID caching. Because local SID cache helps to reduce domain controller workload and network traffic I deleted this registry entry after I checked that cache doesn’t hold deprecate username value anymore.

You can also check solution on official Microsoft support page.

How to get user names and surnames and show them in ASP.NET MVC 4 autocomplete?

Today I was working on some autocompletion with users (name + surname) from Active Directory (from now on AD) in ASP.NET MVC 4 application. I wanted to get suggestions on the fly based on user’s input. I was accessing AD using Lightweight Directory Access Protocol (from now on LDAP).

You have to add two references into your project: System.DirectoryServices.dll and System.DirectoryServices.AccountManagement.dll. In your class you have to use two namespaces:

using System.DirectoryServices.AccountManagement;
using System.DirectoryServices;
In your controller you do something like this:
public JsonResult AutocompleteUsers(string term)
{
    string filter = "(&(&(objectCategory=person)(objectClass=user)(!userAccountControl:1.2.840.113556.1.4.803:=2))(|(givenName={0}*)(sn={1}*)))";
    List persons = new List();

    DirectoryEntry dE = new DirectoryEntry(); //initialization
    {
        //uniquely identifies entry in a networked environment
        dE.Path = "directory entry path"; 

        //username for client authentication
        dE.Username = "directory entry username"; 
        
        //password for client authentication
        dE.Password = "directory entry password";

        //authentication types
        dE.AuthenticationType = AuthenticationTypes.ServerBind; 
    }
    
    DirectorySearcher dS = new DirectorySearcher(dE); //applying search filter
    {
        dS.Filter = String.Format(filter, term, term);
    }

    foreach (SearchResult result in dS.FindAll())
    {
        if (result != null)
        {
            DirectoryEntry entry = result.GetDirectoryEntry();
            
            string name = (entry.Properties["givenName"].Value ?? "").ToString();
            string surname = (entry.Properties["sn"].Value ?? "").ToString();

            if (!String.IsNullOrEmpty(name) && !String.IsNullOrEmpty(surname)) 
            {
                persons.Add(name + " " + surname);
            }
        }
    }

    IEnumerable ipersons = persons.AsEnumerable().OrderBy(p => p);

    return this.Json(ipersons, JsonRequestBehavior.AllowGet);
}
So, firstly you initialize connection with AD. After that you apply search filter. Part (objectCategory=person) returns user and contact objects. Because we only want users, we also need part (!userAccountControl:1.2.840.113556.1.4.803:=2). Contacts don’t have userAccountControl attribute, so this part will always be True for them. With part (objectClass=user) we restrict query to just user object. Part (|(givenName={0}*)(sn={1}*))) restricts query on users which name or surname starts with specified string. After that we loop through search results and get the names.
Collection of names is returned as Json. We must allow get with option JsonRequestBehavior.AllowGet. By default “Json get” is not allowed and without mentioned option exception is produced (Internal Server Error).On client site we have to attach following JavaScript code:

$(document).ready(function () {
    $('#textbox_id').autocomplete({
	source: '@Url.Action("action_name", "controller_name")'
    });
});
Textbox value (inserted string) is automaticaly sent in query string as parameter term. We must not forget to import jQuery scripts:
@Styles.Render("~/Content/themes/base/css")
@Scripts.Render("~/bundles/jquery")    
@Scripts.Render("~/bundles/jqueryui")

Without that callback won’t happen. 🙂