2015-03-23 1 views
0

Depuis quelques jours, j'essaie d'obtenir une authentification basée sur Active Directory personnalisée. Tout fonctionne en théorie mais apparemment ma théorie est fausse. Les utilisateurs qui sont connectés à un domaine écrivent un jeton de chaîne (par exemple un code PIN) dans leur propre champ de propriétés dans Active Directory (peu importe lequel, mais j'ai utilisé primaryInternationISDNNumber pour cela) lors de la connexion à l'application ASP.NET Ce code PIN est toujours généré et écrit par programme. Pour l'expliquer grossièrement, le navigateur Web charge une applet Java qui charge ensuite une DLL native écrite en C++, qui génère et écrit le code PIN dans le champ Active Directory de l'utilisateur actuel. Cette DLL renvoie ensuite le code PIN généré à l'applet qui le transmet ensuite au navigateur, qui effectue un appel AJAX avec les données renvoyées pour lancer l'authentification. L'application, qui a accès à l'AD, lit cette valeur de champ pour l'objet utilisateur qui se connecte et vérifie si elle correspond à celle fournie par l'utilisateur. Si les codes PIN correspondent, l'utilisateur est authentifié avec succès.L'interrogation d'une propriété d'objet Active Directory à partir d'une application ASP.NET renvoie les anciens résultats

C'est le code exemple l'application ASP.NET utilisée pour lire l'AD:

 using (var de = new DirectoryEntry("LDAP://" + domainName)) 
     { 
      using (var adSearch = new DirectorySearcher(de)) 
      { 
       // Get user from active directory. 
       adSearch.Filter = "(sAMAccountName=" + userName.Trim().ToLower(CultureInfo.CurrentCulture) + ")"; 
       var adSearchResult = adSearch.FindOne(); 
       var entry = adSearchResult.GetDirectoryEntry(); 
       var pinCodeProp = entry.Properties["primaryInternationISDNNumber"]; 
       return pinCodeProp != null ? pinCodeProp.Value : string.Empty; 
      } 
     } 

Cela fonctionne très bien, souvent. Mais souvent n'est pas acceptable. Il doit toujours travailler.

Le problème est que l'application ASP.NET obtient parfois la valeur qui était précédemment dans ce champ, pas la valeur réelle. Comme s'il y avait une sorte de cache. J'ai essayé d'ajouter de.UsePropertyCache = false mais cela a donné les mêmes résultats. J'ai créé deux applications console Win32 à des fins de test. L'un écrit le code PIN, l'autre lit le code PIN. Ils toujours fonctionnent très bien!

Je pensais que cela devait être un problème avec le pool d'applications IIS. J'ai donc créé une DLL native qui est chargée par l'application ASP.NET en utilisant Platform Invoke. Cette DLL crée un nouveau thread, appelle CoInitialize et lit le code PIN. Voici le code:

pszFqdn = argv[1]; 
    pszUserName = argv[2]; 
    pszPassword = argv[3]; 

    IADs *pObject = NULL; 
    HRESULT hr = S_OK; 

    hr = CoInitialize(NULL); 
    if (SUCCEEDED(hr)) 
    { 
     hr = ADsOpenObject(pszFqdn, pszUserName, pszPassword, ADS_SECURE_AUTHENTICATION, IID_IADs, (LPVOID*)&pObject); 

     if (SUCCEEDED(hr) && pObject) 
     { 
      VARIANT var; 
      VariantInit(&var); 

      hr = pObject->Get(CComBSTR("primaryInternationalISDNNumber"), &var); 
      if ((SUCCEEDED(hr) && var.bstrVal) || hr == 0x8000500d) 
      { 
       if (hr != 0x8000500d) 
       { 
        // convert the BSTR received to TCHAR array 
        std::wstring wsValue(var.bstrVal, SysStringLen(var.bstrVal)); 
        // copy the received value to somewhere 
        // ... not relevant 
       } 

       VariantClear(&var); 
      } 

      pObject->Release(); 
     } 
    } 

    CoUninitialize(); 

À ma grande surprise énorme et désagréable, ce code après une journée de travail bien, a commencé à retourner les valeurs précédentes, tout comme le code managé avant! Donc maintenant je pensais que je n'étais pas capable d'échapper au pool d'applications IIS et puisque cela doit être un problème avec le pool d'applications IIS, je vais créer une application Windows native que j'exécuterai en utilisant la méthode Process.Start. Je vais retourner mon code PIN au moyen du code de sortie du processus (puisque c'est un entier de toute façon). L'application utilise le code C++ similaire à la DLL ci-dessus.

Donc, je démarre mon application, attendez qu'elle se termine, lisez le code de sortie. Renvoie la mauvaise valeur! Mais d'accord, je dirais que le processus est démarré en utilisant les informations d'identification de l'utilisateur actuel, qui est à nouveau le pool d'applications IIS. Donc, je démarre l'application sous différents titres. Et devinez quoi ... il renvoie à nouveau l'ancienne valeur (?!?!?!).

Et je pensais que Java était l'enfer ... Tout le monde a une idée de ce qui pourrait se passer ici?

+1

Si j'étais vous, je regarderais d'abord la réplication active de votre répertoire. Les deux applications de test étaient supposées s'exécuter sur le même ordinateur et par conséquent, elles utilisaient probablement le même contrôleur de domaine. Le serveur IIS et les clients mettant à jour les valeurs ne le seront pas aléatoirement et, par conséquent, les nouvelles valeurs doivent être répliquées sur les contrôleurs de domaine, ce qui prend du temps en fonction de votre infrastructure. – Ashigore

+0

@Ashigore Les deux applications de test fonctionnent même lorsqu'elles sont exécutées à partir d'ordinateurs différents. Cette application n'a toujours pas été déployée (s'exécutant dans IIS Express). –

+0

@Ashigore aussi, j'ai créé une application de test ASP.NET qui affiche simplement la valeur de AD, après avoir été écrit avec l'application de la console, ou manuellement, n'a pas d'importance ... Le même problème. –

Répondre

0

C'était la réplication en effet.Comme je ne voulais pas forcer la réplication avant de lire le champ (ce qui aurait probablement été une opération coûteuse en temps), j'ai eu l'idée de lire ce champ depuis chaque contrôleur de domaine et de vérifier si l'un d'entre eux correspondait au valeur fournie.

Comme cela pourrait s'avérer utile pour quelqu'un, je l'ai fait en utilisant le code suivant.

 var ctx = new DirectoryContext(
      DirectoryContextType.DirectoryServer, 
      ipAddress, 
      userName, // in the form DOMAIN\UserName or else it would fail for a remote directory server 
      password); 
     var domain = Domain.GetDomain(ctx); 
     var values = new List<string>(); 
     foreach (DomainController dc in domain.DomainControllers) 
     { 
      using (var entry = 
        new DirectoryEntry(
         "LDAP://" + dc.IPAddress, 
         userName, 
         password)) 
      { 
       using (var search = new DirectorySearcher(entry)) 
       { 
        search.Filter = "(&(primaryInternationalISDNNumber=*)(sAMaccountName=" + userName + "))"; 
        var result = search.FindOne(); 
        var de = result.GetDirectoryEntry(); 
        if (de.Properties["primaryInternationalISDNNumber"].Value != null) 
        { 
         values.Add(de.Properties["primaryInternationalISDNNumber"].Value.ToString()); 
        } 
       } 
      } 
     }