Convalida un nome utente e una password su Active Directory?


527

Come posso convalidare un nome utente e una password su Active Directory? Voglio semplicemente verificare se un nome utente e una password sono corretti.

Risposte:


643

Se lavori su .NET 3.5 o versioni successive, puoi utilizzare lo System.DirectoryServices.AccountManagementspazio dei nomi e verificare facilmente le tue credenziali:

// 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");
}

È semplice, è affidabile, è un codice gestito al 100% C # da parte tua: cosa puoi chiedere di più? :-)

Leggi tutto qui:

Aggiornare:

Come indicato in questa altra domanda SO (e nelle sue risposte) , c'è un problema con questa chiamata che potrebbe tornare Trueper vecchie password di un utente. Basta essere consapevoli di questo comportamento e non essere troppo sorpreso se ciò accade :-) (grazie a @MikeGledhill per averlo sottolineato!)


36
Nel mio dominio, ho dovuto specificare pc.ValidateCredentials ("myuser", "mypassword", ContextOptions.Negotiate) o avrei ottenuto System.DirectoryServices.Protocols.DirectoryOperationException: il server non è in grado di gestire le richieste di directory.
Alex Peck,

12
Se una password è scaduta o gli account disabilitati, ValidateCredentials restituirà false. Sfortunatamente, non ti dice perché è stato restituito falso (il che è un peccato perché significa che non posso fare qualcosa di sensato come reindirizzare l'utente per cambiare la loro password).
Chris J,

64
Fai anche attenzione all'account "Ospite": se l'account Ospite a livello di dominio è abilitato, ValidateCredentials restituisce true se gli dai un utente inesistente . Di conseguenza, potresti voler chiamare UserPrinciple.FindByIdentityper vedere se l'ID utente passato esiste prima.
Chris J,

7
@AlexPeck: il motivo per cui è stato necessario eseguire questa operazione (come me) è che .NET utilizza le seguenti tecnologie per impostazione predefinita: LDAP + SSL, Kerberos, quindi RPC. Sospetto che RPC sia disattivato nella tua rete (bene!) E Kerberos non viene effettivamente utilizzato da .NET a meno che tu non lo dica esplicitamente utilizzando ContextOptions.Negotiate.
Brett Veenstra,

5
Tenere presente che se l'utente CAMBIA la propria password di Active Directory, questo codice continuerà ad autenticare felicemente l'utente utilizzando la vecchia password di AD. Sì, davvero. Leggi qui: stackoverflow.com/questions/8949501/…
Mike Gledhill

70

Lo facciamo sulla nostra Intranet

Devi usare System.DirectoryServices;

Ecco le viscere del codice

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();
        }
    }
}

9
Cosa metti in "percorso"? Il nome del dominio? Il nome del server? Il percorso LDAP al dominio? Il percorso LDAP al server?
Ian Boyd,

3
Risposta 1: No, lo eseguiamo come servizio Web in modo che possa essere chiamato da più posizioni nell'app Web principale. Risposta2: Il percorso contiene informazioni LDAP ... LDAP: // DC = domainname1, DC = domainname2, DC = com
DiningPhilanderer

3
Sembrerebbe che ciò possa consentire l'iniezione LDAP. Puoi assicurarti di sfuggire o rimuovere qualsiasi parentesi nello strAccountId
Brain2000,

Questo significa che strPasswordè archiviato in LDAP in testo normale?
Matt Kocaj,

15
Non dovrebbe mai essere necessario chiamare esplicitamente Close()una usingvariabile.
Nyerguds,

62

Diverse soluzioni qui presentate non hanno la possibilità di distinguere tra un utente / password errati e una password che deve essere modificata. Questo può essere fatto nel modo seguente:

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);
            }
        }
    }
}

Se la password dell'utente è errata o l'utente non esiste, l'errore conterrà

"8009030C: LdapErr: DSID-0C0904DC, commento: errore AcceptSecurityContext, dati 52e, v1db1",

se la password dell'utente deve essere modificata, conterrà

"8009030C: LdapErr: DSID-0C0904DC, commento: errore AcceptSecurityContext, dati 773, v1db1"

Il lexc.ServerErrorMessagevalore dei dati è una rappresentazione esadecimale del codice di errore Win32. Questi sono gli stessi codici di errore che verrebbero restituiti invocando altrimenti la chiamata API LogonUser Win32. L'elenco seguente riepiloga un intervallo di valori comuni con valori esadecimali e decimali:

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)

2
Sfortunatamente, alcune installazioni AD non restituiscono il codice di errore secondario LDAP, il che significa che questa soluzione non funzionerà.
Søren Mors,

4
Non dimenticare di aggiungere alcuni riferimenti al progetto: System.DirectoryServiceseSystem.DirectoryServices.Protocols
TomXP411

3
La mia domanda, tuttavia, è questa: come si ottiene il nome del server LDAP? Se stai scrivendo un'applicazione portatile, non puoi aspettarti che l'utente sappia o debba fornire i nomi dei server AD su ogni rete.
TomXP411

1
Ho utenti che si limitano ad accedere a workstation specifiche; come posso specificare la workstation per cui sto provando il login? (workstation1 fallirebbe con i dati 531, workstation2 funzionerebbe bene, per esempio)
akohlsmith

1
Mi sento strano perché non credo che tu abbia abbastanza credito. Questo è un metodo completamente gestito senza il problema della chiamata API Win32 per determinare se "l'utente deve reimpostare la password" che chiaramente nessuna delle altre risposte ha raggiunto. C'è qualche scappatoia in questo metodo che causa un basso tasso di apprezzamento? hmm ...
Lionet Chen

34

soluzione molto semplice utilizzando 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'accesso NativeObject è necessario per rilevare un utente / password errati


4
Questo codice è errato perché esegue anche un controllo di autorizzazione (controlla se l'utente è autorizzato a leggere le informazioni della directory attiva). Il nome utente e la password possono essere validi, ma l'utente non può leggere le informazioni e ottenere un'eccezione. In altre parole, puoi avere un nome utente e una password validi, ma ottenere comunque un'eccezione.
Ian Boyd,

2
sto attualmente chiedendo l' equivalente nativo di PrincipleContext- che esiste solo in .NET 3.5. Ma se stai usando .NET 3.5 o più recente dovresti usarePrincipleContext
Ian Boyd il

28

Sfortunatamente non esiste un modo "semplice" per controllare le credenziali di un utente su AD.

Con ogni metodo presentato finora, potresti ottenere un falso negativo: i crediti di un utente saranno validi, tuttavia AD restituirà false in determinate circostanze:

  • L'utente è tenuto a modificare la password al prossimo accesso.
  • La password dell'utente è scaduta.

ActiveDirectory non ti consentirà di utilizzare LDAP per determinare se una password non è valida a causa del fatto che un utente deve modificare la password o se la loro password è scaduta.

Per determinare la modifica della password o la password scaduta, è possibile chiamare Win32: LogonUser () e controllare il codice di errore di Windows per le seguenti 2 costanti:

  • ERROR_PASSWORD_MUST_CHANGE = 1907
  • ERROR_PASSWORD_EXPIRED = 1330

1
Posso chiederti dove hai preso le devinizioni di Expired e Must_Change ... Non le ho trovate da nessuna parte ma qui :)
mabstrei


Grazie. Stavo cercando di scoprire come la mia convalida fosse sempre falsa. È perché l'utente ha bisogno di cambiare la sua password.
Deise Vicentin,

22

Probabilmente il modo più semplice è PInvoke LogonUser Win32 API.eg

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

Riferimento MSDN qui ...

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

Sicuramente utilizzare il tipo di accesso

LOGON32_LOGON_NETWORK (3)

Questo crea solo un token leggero, perfetto per i controlli AuthN. (altri tipi possono essere utilizzati per creare sessioni interattive ecc.)


Come sottolinea @Alan, l'API LogonUser ha molti tratti utili oltre a una chiamata System.DirectoryServices.
stephbu,

3
@cciotti: no, è sbagliato. Il modo MIGLIORE per autenticare correttamente qualcuno è usare LogonUserAPI come @stephbu write. Tutti gli altri metodi descritti in questo post NON funzioneranno al 100%. Solo una nota, tuttavia, credo che devi essere entrato nel dominio in ordine per chiamare LogonUser.
Alan,

@Alan per generare credenziali devi essere in grado di connetterti al dominio consegnando un account di dominio valido. Tuttavia, sono abbastanza sicuro che la tua macchina non debba necessariamente essere un membro del dominio.
stephbu,

2
L' LogonUserAPI richiede che l'utente abbia Act come parte del privilegio del sistema operativo ; che non è qualcosa che gli utenti ottengono - e non qualcosa che desideri concedere a tutti gli utenti dell'organizzazione. ( msdn.microsoft.com/en-us/library/aa378184(v=vs.85).aspx )
Ian Boyd,

1
LogonUser deve solo agire come parte del sistema operativo per Windows 2000 e versioni successive in base a support.microsoft.com/kb/180548 ... Sembra pulito per Server 2003 e versioni successive.
Chris J,

18

Una soluzione .Net completa consiste nell'utilizzare le classi dallo spazio dei nomi System.DirectoryServices. Consentono di interrogare direttamente un server AD. Ecco un piccolo esempio che farebbe questo:

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

Questo codice si collega direttamente al server AD, utilizzando le credenziali fornite. Se le credenziali non sono valide, searcher.FindOne () genererà un'eccezione. ErrorCode è quello corrispondente all'errore COM "nome utente / password non validi".

Non è necessario eseguire il codice come utente AD. In effetti, lo uso con successo per richiedere informazioni su un server AD, da un client esterno al dominio!


che ne dici di tipi di autenticazione? penso che tu l'abbia dimenticato nel tuo codice sopra. :-) per impostazione predefinita DirectoryEntry.AuthenticationType è impostato su Sicuro, giusto? quel codice non funzionerà su LDAP non protetti (Anonimo o Nessuno forse). ho ragione con questo?
jerbersoft,

L'aspetto negativo di una query su un server AD è che si dispone dell'autorizzazione per eseguire una query sul server AD. Le tue credenziali possono essere valide, ma se non hai i permessi per interrogare AD, otterrai l'errore. Ecco perché è stato creato il cosiddetto Fast Bind ; convalidi le credenziali senza autorizzare la capacità dell'utente di fare qualcosa.
Ian Boyd,

2
ciò non consentirebbe a nessuno di passare nel caso in cui venga lanciata una COMException per qualsiasi altro motivo prima che le credenziali vengano controllate?
Stefan Paul Noack,

11

Ancora un'altra chiamata .NET per autenticare rapidamente le credenziali 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
    }
}

Questa è l'unica soluzione che ha funzionato per me, utilizzando PrincipalContext non ha funzionato per me.
Daniel,

PrincipalContext non valido per una connessione LDAP sicura (aka LDAPS, che utilizza la porta 636
Kiquenet,

10

Prova questo codice (NOTA: segnalato per non funzionare su 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

tranne che dovrai creare la tua eccezione personalizzata per "LogonException"


Non utilizzare la gestione delle eccezioni per restituire informazioni da un metodo. "Nome utente sconosciuto o password errata" non è eccezionale, è un comportamento standard per LogonUser. Restituisci false.
Treb,

sì ... questa era una porta di una vecchia libreria VB6 ... scritta nel 2003 ... (quando uscì .Net per la prima volta)
Charles Bretana,

Se in esecuzione su Windows 2000 questo codice non funzionerà ( support.microsoft.com/kb/180548 )
Ian Boyd,

1
Ripensando a questo. Accesso Il comportamento previsto dell'utente, è lo scopo, è quello di accedere all'utente . Se non riesce a svolgere questo compito, essa È un'eccezione. In effetti, il metodo dovrebbe restituire nulla, non un valore booleano. Inoltre, se hai appena restituito un valore booleano, il consumatore del metodo non ha modo di informare l'utente quale sia stata la ragione dell'errore.
Charles Bretana,

5

Se sei bloccato con .NET 2.0 e il codice gestito, ecco un altro modo che funziona con gli account locali e di dominio:

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;
}   

Funziona bene con gli account locali della macchina che lancia la sceneggiatura
eka808,

A proposito, questo metodo è necessario per rendere pubblica questa funzione SecureString ToSecureString statica (stringa PwString) {char [] PasswordChars = PwString.ToCharArray (); SecureString Password = new SecureString (); foreach (char c in PasswordChars) Password.AppendChar (c); ProcessStartInfo foo = new ProcessStartInfo (); foo.Password = Password; return foo.Password; }
eka808,

Al contrario, si dovrebbe comunque utilizzare SecureString per le password. WPF PasswordBox lo supporta.
Stephen Drew,

5

L'autenticazione di Windows può non riuscire per vari motivi: un nome utente o una password errati, un account bloccato, una password scaduta e altro. Per distinguere tra questi errori, chiamare la funzione API LogonUser tramite P / Invoke e controllare il codice di errore se la funzione restituisce 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()
    }
}

Esempio di utilizzo:

try
{
    Win32Authentication.AuthenticateUser("EXAMPLE\\user", "P@ssw0rd");
    // Or: Win32Authentication.AuthenticateUser("user@example.com", "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;
    }
}

Nota: LogonUser richiede una relazione di fiducia con il dominio con il quale stai convalidando.


puoi spiegare perché la tua risposta è migliore della risposta più votata?
Mohammad Ali,

1
@MohammadAli: se devi sapere perché la convalida delle credenziali non è riuscita (credenziali errate, un account bloccato, una password scaduta, ecc.), La funzione API LogonUser ti dirà. Al contrario, il metodo PrincipalContext.ValidateCredentials (secondo i commenti sulla risposta di marc_s) non lo farà; restituisce false in tutti questi casi. D'altro canto, LogonUser richiede una relazione di trust con il dominio, ma PrincipalContext.ValidateCredentials (credo) no.
Michael Liu,

2

La mia semplice funzione

 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;
        }
    }

1

Ecco la mia soluzione di autenticazione completa per il tuo riferimento.

Innanzitutto, aggiungi i seguenti quattro riferimenti

 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 = "";

        }
    }
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.