Valider un nom d'utilisateur et un mot de passe par rapport à Active Directory?

527

Comment puis-je valider un nom d'utilisateur et un mot de passe par rapport à Active Directory? Je veux simplement vérifier si un nom d'utilisateur et un mot de passe sont corrects.

Marc
la source

Réponses:

643

Si vous travaillez sur .NET 3.5 ou plus récent, vous pouvez utiliser l' System.DirectoryServices.AccountManagementespace de noms et vérifier facilement vos informations d'identification:

// create a "principal context" - e.g. your domain (could be machine, too)
using(PrincipalContext pc = new PrincipalContext(ContextType.Domain, "YOURDOMAIN"))
{
    // validate the credentials
    bool isValid = pc.ValidateCredentials("myuser", "mypassword");
}

C'est simple, c'est fiable, c'est du code 100% C # géré de votre côté - que demander de plus? :-)

Lisez tout a propos de ça ici:

Mise à jour:

Comme indiqué dans cette autre question SO (et ses réponses) , il y a un problème avec cet appel qui peut renvoyer les Trueanciens mots de passe d'un utilisateur. Soyez juste conscient de ce comportement et ne soyez pas trop surpris si cela se produit :-) (merci à @MikeGledhill de l'avoir signalé!)

marc_s
la source
36
Dans mon domaine, je devais spécifier pc.ValidateCredentials ("myuser", "mypassword", ContextOptions.Negotiate) ou j'obtiendrais System.DirectoryServices.Protocols.DirectoryOperationException: le serveur ne peut pas gérer les demandes d'annuaire.
Alex Peck
12
Si un mot de passe a expiré ou si les comptes sont désactivés, alors ValidateCredentials retournera false. Malheureusement, cela ne vous dit pas pourquoi il est retourné faux (ce qui est dommage car cela signifie que je ne peux pas faire quelque chose de sensé comme rediriger l'utilisateur pour changer son mot de passe).
Chris J
65
Méfiez-vous également du compte «Invité» - si le compte Invité au niveau du domaine est activé, ValidateCredentials renvoie true si vous lui donnez un utilisateur inexistant . Par conséquent, vous souhaiterez peut-être appeler UserPrinciple.FindByIdentitypour voir si l'ID utilisateur transmis existe en premier.
Chris J
7
@AlexPeck: la raison pour laquelle vous avez dû faire cela (comme moi) était que .NET utilise par défaut les technologies suivantes: LDAP + SSL, Kerberos, puis RPC. Je soupçonne que RPC est désactivé dans votre réseau (bon!) Et Kerberos n'est pas réellement utilisé par .NET sauf si vous le lui dites explicitement en utilisant ContextOptions.Negotiate.
Brett Veenstra
5
N'oubliez pas que si l'utilisateur MODIFIE son mot de passe Active Directory, ce morceau de code continuera à authentifier l'utilisateur avec plaisir en utilisant son ancien mot de passe AD. Ouais, vraiment. Lisez ici: stackoverflow.com/questions/8949501/…
Mike Gledhill
70

Nous le faisons sur notre intranet

Vous devez utiliser System.DirectoryServices;

Voici les tripes du code

using (DirectoryEntry adsEntry = new DirectoryEntry(path, strAccountId, strPassword))
{
    using (DirectorySearcher adsSearcher = new DirectorySearcher(adsEntry))
    {
        //adsSearcher.Filter = "(&(objectClass=user)(objectCategory=person))";
        adsSearcher.Filter = "(sAMAccountName=" + strAccountId + ")";

        try
        {
            SearchResult adsSearchResult = adsSearcher.FindOne();
            bSucceeded = true;

            strAuthenticatedBy = "Active Directory";
            strError = "User has been authenticated by Active Directory.";
        }
        catch (Exception ex)
        {
            // Failed to authenticate. Most likely it is caused by unknown user
            // id or bad strPassword.
            strError = ex.Message;
        }
        finally
        {
            adsEntry.Close();
        }
    }
}
DînerPhilanderer
la source
9
Que mettez-vous dans "chemin"? Le nom du domaine? Le nom du serveur? Le chemin LDAP vers le domaine? Le chemin LDAP vers le serveur?
Ian Boyd,
3
Réponse 1: Non, nous l'exécutons en tant que service Web afin qu'il puisse être appelé à partir de plusieurs emplacements dans l'application Web principale. Answer2: Le chemin contient des informations LDAP ... LDAP: // DC = nom_domaine1, DC = nom_domaine2, DC = com
DiningPhilanderer
3
Il semblerait que cela pourrait permettre l'injection LDAP. Vous voudrez peut-être vous assurer d'échapper ou de supprimer toutes les parenthèses dans strAccountId
Brain2000
Cela signifie-t-il qu'il strPasswordest stocké dans LDAP en texte brut?
Matt Kocaj
15
Il ne devrait jamais être nécessaire d'appeler explicitement Close()une usingvariable.
Nyerguds
62

Plusieurs solutions présentées ici ne permettent pas de faire la différence entre un mauvais utilisateur / mot de passe et un mot de passe qui doit être changé. Cela peut se faire de la manière suivante:

using System;
using System.DirectoryServices.Protocols;
using System.Net;

namespace ProtocolTest
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                LdapConnection connection = new LdapConnection("ldap.fabrikam.com");
                NetworkCredential credential = new NetworkCredential("user", "password");
                connection.Credential = credential;
                connection.Bind();
                Console.WriteLine("logged in");
            }
            catch (LdapException lexc)
            {
                String error = lexc.ServerErrorMessage;
                Console.WriteLine(lexc);
            }
            catch (Exception exc)
            {
                Console.WriteLine(exc);
            }
        }
    }
}

Si le mot de passe des utilisateurs est incorrect ou si l'utilisateur n'existe pas, l'erreur contiendra

"8009030C: LdapErr: DSID-0C0904DC, commentaire: erreur AcceptSecurityContext, données 52e, v1db1",

si le mot de passe des utilisateurs doit être changé, il contiendra

"8009030C: LdapErr: DSID-0C0904DC, commentaire: erreur AcceptSecurityContext, données 773, v1db1"

La lexc.ServerErrorMessagevaleur des données est une représentation hexadécimale du code d'erreur Win32. Ce sont les mêmes codes d'erreur qui seraient retournés en invoquant autrement l'appel API Win32 LogonUser. La liste ci-dessous résume une plage de valeurs communes avec des valeurs hexadécimales et décimales:

525 user not found ​(1317)
52e invalid credentials ​(1326)
530 not permitted to logon at this time (1328)
531 not permitted to logon at this workstation (1329)
532 password expired ​(1330)
533 account disabled ​(1331) 
701 account expired ​(1793)
773 user must reset password (1907)
775 user account locked (1909)
Søren Mors
la source
2
Malheureusement, certaines installations AD ne renvoient pas le sous-code d'erreur LDAP, ce qui signifie que cette solution ne fonctionnera pas.
Søren Mors
4
N'oubliez pas d'ajouter quelques références au projet: System.DirectoryServicesetSystem.DirectoryServices.Protocols
TomXP411
3
Ma question, cependant, est la suivante: comment obtenir le nom du serveur LDAP? Si vous écrivez une application portable, vous ne pouvez pas vous attendre à ce que l'utilisateur connaisse ou doive fournir les noms des serveurs AD sur chaque réseau.
TomXP411
1
J'ai des utilisateurs qui sont limités à se connecter à des postes de travail spécifiques; comment spécifier le poste de travail pour lequel j'essaie de me connecter? (workstation1 échouerait avec les données 531, workstation2 fonctionnerait bien, par exemple)
akohlsmith
1
Je me sens bizarre car je ne pense pas que vous obtenez suffisamment de crédit. Il s'agit d'une méthode entièrement gérée sans le problème de l'appel de l'API Win32 pour déterminer si "l'utilisateur doit réinitialiser le mot de passe", ce qui clairement aucune des autres réponses n'a été obtenue. Y a-t-il une faille dans cette méthode qui cause son faible taux d'appréciation? hmm ...
Lionet Chen
34

solution très simple utilisant DirectoryServices:

using System.DirectoryServices;

//srvr = ldap server, e.g. LDAP://domain.com
//usr = user name
//pwd = user password
public bool IsAuthenticated(string srvr, string usr, string pwd)
{
    bool authenticated = false;

    try
    {
        DirectoryEntry entry = new DirectoryEntry(srvr, usr, pwd);
        object nativeObject = entry.NativeObject;
        authenticated = true;
    }
    catch (DirectoryServicesCOMException cex)
    {
        //not authenticated; reason why is in cex
    }
    catch (Exception ex)
    {
        //not authenticated due to some other exception [this is optional]
    }

    return authenticated;
}

l'accès NativeObject est requis pour détecter un mauvais utilisateur / mot de passe

Steven A. Lowe
la source
4
Ce code est mauvais car il effectue également une vérification d'autorisation (vérifiez si l'utilisateur est autorisé à lire les informations de l'annuaire actif). Le nom d'utilisateur et le mot de passe peuvent être valides, mais l'utilisateur n'est pas autorisé à lire les informations - et obtenir une exception. En d'autres termes, vous pouvez avoir un nom d'utilisateur et un mot de passe valides, mais toujours obtenir une exception.
Ian Boyd
2
je suis en train de demander l' équivalent natif de PrincipleContext- qui n'existe que dans .NET 3.5. Mais si vous utilisez .NET 3.5 ou plus récent, vous devez utiliserPrincipleContext
Ian Boyd
28

Malheureusement, il n'existe aucun moyen "simple" de vérifier les informations d'identification d'un utilisateur sur AD.

Avec chaque méthode présentée jusqu'à présent, vous pouvez obtenir un faux négatif: les crédits d'un utilisateur seront valides, mais AD retournera faux dans certaines circonstances:

  • L'utilisateur doit changer de mot de passe à la prochaine ouverture de session.
  • Le mot de passe de l'utilisateur a expiré.

ActiveDirectory ne vous permettra pas d'utiliser LDAP pour déterminer si un mot de passe n'est pas valide car un utilisateur doit changer de mot de passe ou si son mot de passe a expiré.

Pour déterminer le changement de mot de passe ou le mot de passe expiré, vous pouvez appeler Win32: LogonUser () et vérifier le code d'erreur Windows pour les 2 constantes suivantes:

  • ERROR_PASSWORD_MUST_CHANGE = 1907
  • ERROR_PASSWORD_EXPIRED = 1330
Alan
la source
1
Puis-je demander où vous avez obtenu les définitions pour Expired et Must_Change ... Je ne les ai trouvées nulle part mais ici :)
mabstrei
1
À partir d'un article MSDN: msdn.microsoft.com/en-us/library/windows/desktop/…
Alan
Merci. J'essayais de savoir comment ma validation retournait fausse tout le temps. C'est parce que l'utilisateur doit changer son mot de passe.
Deise Vicentin
22

Le moyen probablement le plus simple consiste à PInvoke LogonUser Win32 API.eg

http://www.pinvoke.net/default.aspx/advapi32/LogonUser.html

Référence MSDN ici ...

http://msdn.microsoft.com/en-us/library/aa378184.aspx

Je veux vraiment utiliser le type d'ouverture de session

LOGON32_LOGON_NETWORK (3)

Cela crée un jeton léger uniquement - parfait pour les vérifications AuthN. (d'autres types peuvent être utilisés pour créer des sessions interactives, etc.)

stephbu
la source
Comme le souligne @Alan, l'API LogonUser possède de nombreux traits utiles au-delà d'un appel System.DirectoryServices.
stephbu
3
@cciotti: Non, c'est faux. La MEILLEURE façon d'authentifier correctement quelqu'un est d'utiliser LogonUserAPI comme écriture @stephbu. Toutes les autres méthodes décrites dans cet article ne fonctionneront PAS à 100%. Juste une note cependant, je crois que vous devez être connecté au domaine pour appeler LogonUser.
Alan
@Alan pour générer des informations d'identification, vous devez pouvoir vous connecter au domaine en remettant un compte de domaine valide. Cependant, je suis presque sûr que votre machine n'a pas nécessairement besoin d'être membre du domaine.
stephbu
2
L' LogonUserAPI oblige l'utilisateur à avoir la loi dans le cadre du privilège du système d' exploitation; ce qui n'est pas quelque chose que les utilisateurs obtiennent - et non quelque chose que vous souhaitez accorder à chaque utilisateur de l'organisation. ( msdn.microsoft.com/en-us/library/aa378184(v=vs.85).aspx )
Ian Boyd
1
LogonUser n'a besoin que d' agir en tant que partie du système d'exploitation pour Windows 2000 et inférieur selon support.microsoft.com/kb/180548 ... Il semble propre pour Server 2003 et supérieur.
Chris J
18

Une solution .Net complète consiste à utiliser les classes de l'espace de noms System.DirectoryServices. Ils permettent d'interroger directement un serveur AD. Voici un petit échantillon qui ferait cela:

using (DirectoryEntry entry = new DirectoryEntry())
{
    entry.Username = "here goes the username you want to validate";
    entry.Password = "here goes the password";

    DirectorySearcher searcher = new DirectorySearcher(entry);

    searcher.Filter = "(objectclass=user)";

    try
    {
        searcher.FindOne();
    }
    catch (COMException ex)
    {
        if (ex.ErrorCode == -2147023570)
        {
            // Login or password is incorrect
        }
    }
}

// FindOne() didn't throw, the credentials are correct

Ce code se connecte directement au serveur AD, à l'aide des informations d'identification fournies. Si les informations d'identification ne sont pas valides, searcher.FindOne () lèvera une exception. Le code d'erreur est celui correspondant à l'erreur COM "nom d'utilisateur / mot de passe invalide".

Vous n'avez pas besoin d'exécuter le code en tant qu'utilisateur AD. En fait, je l'utilise avec succès pour interroger des informations sur un serveur AD, à partir d'un client en dehors du domaine!

Mathieu Garstecki
la source
que diriez-vous des types d'authentification? je pense que vous l'avez oublié dans votre code ci-dessus. :-) par défaut, DirectoryEntry.AuthenticationType est défini sur Secured right? ce code ne fonctionnera pas sur les LDAP non sécurisés (Anonymous ou None peut-être). suis-je correct avec cela?
jerbersoft
L' inconvénient de l' interrogation d' un serveur AD est que vous êtes autorisé à interroger le serveur AD. Vos informations d'identification peuvent être valides, mais si vous n'êtes pas autorisé à interroger AD, vous obtiendrez l'erreur. C'est pourquoi la soi-disant Fast Bind a été créée; vous validez les informations d'identification sans autoriser l'utilisateur à faire quelque chose.
Ian Boyd
2
cela ne permettrait-il à personne de passer au cas où une exception COMException serait levée pour une autre raison avant la vérification des informations d'identification?
Stefan Paul Noack
11

Encore un autre appel .NET pour authentifier rapidement les informations d'identification LDAP:

using System.DirectoryServices;

using(var DE = new DirectoryEntry(path, username, password)
{
    try
    {
        DE.RefreshCache(); // This will force credentials validation
    }
    catch (COMException ex)
    {
        // Validation failed - handle how you want
    }
}
palswim
la source
C'est la seule solution qui a fonctionné pour moi, l'utilisation de PrincipalContext n'a pas fonctionné pour moi.
Daniel
PrincipalContext non valide pour une connexion LDAP sécurisée (alias LDAPS, qui utilise le port 636
Kiquenet
10

Essayez ce code (REMARQUE: signalé comme ne fonctionnant pas sur Windows Server 2000)

#region NTLogonUser
#region Direct OS LogonUser Code
[DllImport( "advapi32.dll")]
private static extern bool LogonUser(String lpszUsername, 
    String lpszDomain, String lpszPassword, int dwLogonType, 
    int dwLogonProvider, out int phToken);

[DllImport("Kernel32.dll")]
private static extern int GetLastError();

public static bool LogOnXP(String sDomain, String sUser, String sPassword)
{
   int token1, ret;
   int attmpts = 0;

   bool LoggedOn = false;

   while (!LoggedOn && attmpts < 2)
   {
      LoggedOn= LogonUser(sUser, sDomain, sPassword, 3, 0, out token1);
      if (LoggedOn) return (true);
      else
      {
         switch (ret = GetLastError())
         {
            case (126): ; 
               if (attmpts++ > 2)
                  throw new LogonException(
                      "Specified module could not be found. error code: " + 
                      ret.ToString());
               break;

            case (1314): 
               throw new LogonException(
                  "Specified module could not be found. error code: " + 
                      ret.ToString());

            case (1326): 
               // edited out based on comment
               //  throw new LogonException(
               //   "Unknown user name or bad password.");
            return false;

            default: 
               throw new LogonException(
                  "Unexpected Logon Failure. Contact Administrator");
              }
          }
       }
   return(false);
}
#endregion Direct Logon Code
#endregion NTLogonUser

sauf que vous devrez créer votre propre exception personnalisée pour "LogonException"

Charles Bretana
la source
N'utilisez pas la gestion des exceptions pour renvoyer des informations à partir d'une méthode. "Nom d'utilisateur inconnu ou mauvais mot de passe" n'est pas exceptionnel, c'est un comportement standard pour LogonUser. Retournez simplement faux.
Treb
oui ... c'était un port d'une ancienne bibliothèque VB6 ... écrit en 2003 ou alors ... (quand .Net est sorti pour la première fois)
Charles Bretana
Si vous exécutez sous Windows 2000, ce code ne fonctionnera pas ( support.microsoft.com/kb/180548 )
Ian Boyd
1
Repenser cela. Ouverture de session Le comportement attendu de l'utilisateur, son objectif, est de se connecter à l'utilisateur . Si elle ne parvient pas à exécuter cette tâche, il EST une exception. En fait, la méthode doit retourner void, pas un booléen. De plus, si vous venez de renvoyer un booléen, le consommateur de la méthode n'a aucun moyen d'informer l'utilisateur de la raison de l'échec.
Charles Bretana
5

Si vous êtes bloqué avec .NET 2.0 et le code managé, voici une autre méthode qui fonctionne avec les comptes locaux et de domaine:

using System;
using System.Collections.Generic;
using System.Text;
using System.Security;
using System.Diagnostics;

static public bool Validate(string domain, string username, string password)
{
    try
    {
        Process proc = new Process();
        proc.StartInfo = new ProcessStartInfo()
        {
            FileName = "no_matter.xyz",
            CreateNoWindow = true,
            WindowStyle = ProcessWindowStyle.Hidden,
            WorkingDirectory = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData),
            UseShellExecute = false,
            RedirectStandardError = true,
            RedirectStandardOutput = true,
            RedirectStandardInput = true,
            LoadUserProfile = true,
            Domain = String.IsNullOrEmpty(domain) ? "" : domain,
            UserName = username,
            Password = Credentials.ToSecureString(password)
        };
        proc.Start();
        proc.WaitForExit();
    }
    catch (System.ComponentModel.Win32Exception ex)
    {
        switch (ex.NativeErrorCode)
        {
            case 1326: return false;
            case 2: return true;
            default: throw ex;
        }
    }
    catch (Exception ex)
    {
        throw ex;
    }

    return false;
}   
chauwel
la source
Fonctionne bien avec les comptes locaux de la machine dont il lance le script
eka808
BTW, cette méthode est nécessaire pour rendre cela public public SecureString ToSecureString (string PwString) {char [] PasswordChars = PwString.ToCharArray (); Mot de passe SecureString = nouveau SecureString (); foreach (char c dans PasswordChars) Password.AppendChar (c); ProcessStartInfo foo = new ProcessStartInfo (); foo.Password = Mot de passe; return foo.Password; }
eka808
Au contraire, il faut quand même utiliser SecureString pour les mots de passe. WPF PasswordBox le prend en charge.
Stephen Drew
5

L'authentification Windows peut échouer pour diverses raisons: un nom d'utilisateur ou un mot de passe incorrect, un compte verrouillé, un mot de passe expiré, etc. Pour faire la distinction entre ces erreurs, appelez la fonction API LogonUser via P / Invoke et vérifiez le code d'erreur si la fonction renvoie false:

using System;
using System.ComponentModel;
using System.Runtime.InteropServices;

using Microsoft.Win32.SafeHandles;

public static class Win32Authentication
{
    private class SafeTokenHandle : SafeHandleZeroOrMinusOneIsInvalid
    {
        private SafeTokenHandle() // called by P/Invoke
            : base(true)
        {
        }

        protected override bool ReleaseHandle()
        {
            return CloseHandle(this.handle);
        }
    }

    private enum LogonType : uint
    {
        Network = 3, // LOGON32_LOGON_NETWORK
    }

    private enum LogonProvider : uint
    {
        WinNT50 = 3, // LOGON32_PROVIDER_WINNT50
    }

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern bool CloseHandle(IntPtr handle);

    [DllImport("advapi32.dll", SetLastError = true)]
    private static extern bool LogonUser(
        string userName, string domain, string password,
        LogonType logonType, LogonProvider logonProvider,
        out SafeTokenHandle token);

    public static void AuthenticateUser(string userName, string password)
    {
        string domain = null;
        string[] parts = userName.Split('\\');
        if (parts.Length == 2)
        {
            domain = parts[0];
            userName = parts[1];
        }

        SafeTokenHandle token;
        if (LogonUser(userName, domain, password, LogonType.Network, LogonProvider.WinNT50, out token))
            token.Dispose();
        else
            throw new Win32Exception(); // calls Marshal.GetLastWin32Error()
    }
}

Exemple d'utilisation:

try
{
    Win32Authentication.AuthenticateUser("EXAMPLE\\user", "P@ssw0rd");
    // Or: Win32Authentication.AuthenticateUser("[email protected]", "P@ssw0rd");
}
catch (Win32Exception ex)
{
    switch (ex.NativeErrorCode)
    {
        case 1326: // ERROR_LOGON_FAILURE (incorrect user name or password)
            // ...
        case 1327: // ERROR_ACCOUNT_RESTRICTION
            // ...
        case 1330: // ERROR_PASSWORD_EXPIRED
            // ...
        case 1331: // ERROR_ACCOUNT_DISABLED
            // ...
        case 1907: // ERROR_PASSWORD_MUST_CHANGE
            // ...
        case 1909: // ERROR_ACCOUNT_LOCKED_OUT
            // ...
        default: // Other
            break;
    }
}

Remarque: LogonUser nécessite une relation d'approbation avec le domaine contre lequel vous validez.

Michael Liu
la source
pouvez-vous expliquer pourquoi votre réponse est meilleure que la réponse la plus votée?
Mohammad Ali
1
@MohammadAli: Si vous avez besoin de savoir pourquoi la validation des informations d'identification a échoué (informations d'identification incorrectes, compte verrouillé, mot de passe expiré, etc.), la fonction API LogonUser vous le dira. En revanche, la méthode PrincipalContext.ValidateCredentials (selon les commentaires sur la réponse de marc_s) ne le fera pas; il retourne faux dans tous ces cas. D'un autre côté, LogonUser nécessite une relation de confiance avec le domaine, mais PrincipalContext.ValidateCredentials (je pense) ne le fait pas.
Michael Liu
2

Ma fonction simple

 private bool IsValidActiveDirectoryUser(string activeDirectoryServerDomain, string username, string password)
    {
        try
        {
            DirectoryEntry de = new DirectoryEntry("LDAP://" + activeDirectoryServerDomain, username + "@" + activeDirectoryServerDomain, password, AuthenticationTypes.Secure);
            DirectorySearcher ds = new DirectorySearcher(de);
            ds.FindOne();
            return true;
        }
        catch //(Exception ex)
        {
            return false;
        }
    }
hossein andarkhora
la source
1

Voici ma solution d'authentification complète pour votre référence.

Tout d'abord, ajoutez les quatre références suivantes

 using System.DirectoryServices;
 using System.DirectoryServices.Protocols;
 using System.DirectoryServices.AccountManagement;
 using System.Net; 

private void AuthUser() { 


      try{
            string Uid = "USER_NAME";
            string Pass = "PASSWORD";
            if (Uid == "")
            {
                MessageBox.Show("Username cannot be null");
            }
            else if (Pass == "")
            {
                MessageBox.Show("Password cannot be null");
            }
            else
            {
                LdapConnection connection = new LdapConnection("YOUR DOMAIN");
                NetworkCredential credential = new NetworkCredential(Uid, Pass);
                connection.Credential = credential;
                connection.Bind();

                // after authenticate Loading user details to data table
                PrincipalContext ctx = new PrincipalContext(ContextType.Domain);
                UserPrincipal user = UserPrincipal.FindByIdentity(ctx, Uid);
                DirectoryEntry up_User = (DirectoryEntry)user.GetUnderlyingObject();
                DirectorySearcher deSearch = new DirectorySearcher(up_User);
                SearchResultCollection results = deSearch.FindAll();
                ResultPropertyCollection rpc = results[0].Properties;
                DataTable dt = new DataTable();
                DataRow toInsert = dt.NewRow();
                dt.Rows.InsertAt(toInsert, 0);

                foreach (string rp in rpc.PropertyNames)
                {
                    if (rpc[rp][0].ToString() != "System.Byte[]")
                    {
                        dt.Columns.Add(rp.ToString(), typeof(System.String));

                        foreach (DataRow row in dt.Rows)
                        {
                            row[rp.ToString()] = rpc[rp][0].ToString();
                        }

                    }  
                }
             //You can load data to grid view and see for reference only
                 dataGridView1.DataSource = dt;


            }
        } //Error Handling part
        catch (LdapException lexc)
        {
            String error = lexc.ServerErrorMessage;
            string pp = error.Substring(76, 4);
            string ppp = pp.Trim();

            if ("52e" == ppp)
            {
                MessageBox.Show("Invalid Username or password, contact ADA Team");
            }
            if ("775​" == ppp)
            {
                MessageBox.Show("User account locked, contact ADA Team");
            }
            if ("525​" == ppp)
            {
                MessageBox.Show("User not found, contact ADA Team");
            }
            if ("530" == ppp)
            {
                MessageBox.Show("Not permitted to logon at this time, contact ADA Team");
            }
            if ("531" == ppp)
            {
                MessageBox.Show("Not permitted to logon at this workstation, contact ADA Team");
            }
            if ("532" == ppp)
            {
                MessageBox.Show("Password expired, contact ADA Team");
            }
            if ("533​" == ppp)
            {
                MessageBox.Show("Account disabled, contact ADA Team");
            }
            if ("533​" == ppp)
            {
                MessageBox.Show("Account disabled, contact ADA Team");
            }



        } //common error handling
        catch (Exception exc)
        {
            MessageBox.Show("Invalid Username or password, contact ADA Team");

        }

        finally {
            tbUID.Text = "";
            tbPass.Text = "";

        }
    }
Gayan Chinthaka Dharmarathna
la source