0

I'm writing some kind of a mini AD tool (with VS-C#) to our organization and got into an issue.

I have a main function that searches the user (when I click on it in a listview) and some functions that manipulate the user's object.

public DirectoryEntry GetUser(string username)
    {
        try
        {
            Forest currentForest = Forest.GetCurrentForest();
            GlobalCatalog gc = currentForest.FindGlobalCatalog();

            using (DirectorySearcher searcher = gc.GetDirectorySearcher())
            {
                searcher.Filter = "(&((&(objectCategory=Person)(objectClass=User)))(samaccountname=" + username + "*))";
                SearchResult results = searcher.FindOne();
                if (!(results == null))
                {

                    DirectoryEntry de = new DirectoryEntry(results.Path, strAdminUser, strAdminPass, AuthenticationTypes.Secure);
                    de.RefreshCache(new string[] { "canonicalName" });
                    de.Path = de.Properties["canonicalName"].Value.ToString();
                    de.CommitChanges();
                    return de;
                }
                else
                {
                    return null;
                }
            }
        }
        catch (DirectoryServicesCOMException e)
        {
            System.Windows.Forms.MessageBox.Show(e.Message);
            return null;
        }
    }

and here's an example of a function that checks if the user is locked:

public bool IsUserLocked(string username)
    {
         try
         {
             DirectoryEntry de = GetUser(username);
             string attName = "msDS-User-Account-Control-Computed";
             de.RefreshCache(new string[] { attName });
             const int UF_LOCKOUT = 0x0010;
             int userFlags = /*(int)*/Convert.ToInt32(de.Properties[attName].Value);
             if ((userFlags & UF_LOCKOUT) == UF_LOCKOUT)
             {
                 return true;
             }
             de.Dispose();
             return false;
         }
         catch (DirectoryServicesCOMException e)
         {
            System.Windows.Forms.MessageBox.Show(e.Message);
            return false;
         }
      }

The function that checks the locked status of a user always fails with an error: "Unspecified error", but if I'm not changing the Directory Entry's path in the first function I get "The server is unwilling to process the request" error (I'm using proper service username and password with all the permissions needed) but still it happens.

Can someone spot the issue?

Kara
  • 6,115
  • 16
  • 50
  • 57
  • do a google search.. there are several examples on the web as well as on Stackoverflow in regards to checking the locked status using C# check here for starters - http://stackoverflow.com/questions/2005637/how-to-determine-if-user-account-is-enabled-or-disabled – MethodMan Aug 15 '16 at 16:37

2 Answers2

0

How about using System.DirectoryServices.AccountManagement namespace? If you have no issue using the new namespace, there's a simpler way to check if user account is locked and unlock if needed.

public bool IsUserLocked (string username)
{
   using(PrincipalContext ctx = new PrincipalContext(ContextType.Domain, "yourdomain.com")
    {
       using (UserPrincipal user = UserPrincipal.FindByIdentity(ctx, username)
       {
          if (user != null) return user.IsAccountLockedOut();
       }
    }
    return null;
}

And similarly, you can unlock the user account if needed.

...

if (user != null)
{
   user.UnlockAccount();
   user.Save();
}
smr5
  • 2,593
  • 6
  • 39
  • 66
  • We have a domain and subdomain in the organization so I first need to determine what is the user's domain then use your example.... – Eliran Abudi Aug 17 '16 at 16:02
  • Do you have the `username` prior to knowing the domain? – smr5 Aug 17 '16 at 16:54
  • Yes, I get a list on a listview with users matching a search criteria. I use this to get the list: – Eliran Abudi Aug 17 '16 at 17:04
  • `code` public List GetADUserDetails(string username) { List resultList = new List(); Forest currentForest = Forest.GetCurrentForest(); GlobalCatalog gc = currentForest.FindGlobalCatalog(); using (DirectorySearcher searcher = gc.GetDirectorySearcher()) { searcher.Filter = "(&((&(objectCategory=Person)(objectClass=User)))(samaccountname=" + username + "*))"; SearchResultCollection allUsers = searcher.FindAll(); – Eliran Abudi Aug 17 '16 at 17:04
  • foreach (SearchResult result in allUsers) { resultList.Add(result); } searcher.Dispose(); gc.Dispose(); currentForest.Dispose(); return resultList; } } – Eliran Abudi Aug 17 '16 at 17:05
  • You can grab the domain name for a logged in user this way `string domainName; domainName = System.Environment.UserDomainName;` Is that something you're looking for? – smr5 Aug 17 '16 at 17:15
  • Well my proggy is a bit like a mini A-D... I search a user using a matching criteria and get a list on a listview The list view shows 3 fields: 1) User name 2) Display Name 3) eMail address I want to click on one of the results (the one I need to perform some actions) and see at the same screen if it's locked, disabled and if the user must change password at next logon... But as I click on the result the program collapse with the above exception. The above code of in my question shows the functions that should grab the user's object and show the needed info, but it seems wrong – Eliran Abudi Aug 17 '16 at 17:28
0

Got it...

This has solved my issue: de.Path = results.Path.Replace("GC://DCNAME.", "LDAP://"); Since I'm searcing on the Global Catalog, I had to replace a portion in the path to match it to the correct path:

public DirectoryEntry GetUser(string username)
    {
        try
        {
            Forest currentForest = Forest.GetCurrentForest();
            GlobalCatalog gc = currentForest.FindGlobalCatalog();

            using (DirectorySearcher searcher = gc.GetDirectorySearcher())
            {
                searcher.Filter = "(&((&(objectCategory=Person)(objectClass=User)))(samaccountname=" + username + "*))";
                SearchResult results = searcher.FindOne();
                if (!(results == null))
                {

                    DirectoryEntry de = new DirectoryEntry(results.Path, strAdminUser, strAdminPass, AuthenticationTypes.Secure);
                    de = new DirectoryEntry(results.Path);
                    de.Path = results.Path.Replace("GC://DCNAME.", "LDAP://");
                    de.CommitChanges();
                    //System.Windows.Forms.MessageBox.Show(de.Path);
                    return de;
                }
                else
                {
                    return null;
                }
            }
        }
        catch (DirectoryServicesCOMException e)
        {
            System.Windows.Forms.MessageBox.Show(e.Message);
            return null;
        }
    }

Now the path returns to the function called GetUser in the correct format :)