Al probar nuestra aplicación .NET 4.0 en .NET 4.5, hemos encontrado un problema con el método FindByIdentity
para UserPrincipal
. El siguiente código funciona cuando se ejecuta en un tiempo de ejecución .NET 4.0, pero falla en .NET 4.5:Error .NET 4.5 en UserPrincipal.FindByIdentity (System.DirectoryServices.AccountManagement)
[Test]
public void TestIsAccountLockedOut()
{
const string activeDirectoryServer = "MyActiveDirectoryServer";
const string activeDirectoryLogin = "[email protected]";
const string activeDirectoryPassword = "MyADAccountPassword";
const string userAccountToTest = "[email protected]";
const string userPasswordToTest = "WRONGPASSWORD";
var principalContext = new PrincipalContext(ContextType.Domain, activeDirectoryServer, activeDirectoryLogin, activeDirectoryPassword);
var isAccountLockedOut = false;
var isAuthenticated = principalContext.ValidateCredentials(userAccountToTest, userPasswordToTest, principalContext.Options);
if (!isAuthenticated)
{
// System.DirectoryServices.AccountManagement.PrincipalOperationException : Information about the domain could not be retrieved (1355).
using (var user = UserPrincipal.FindByIdentity(principalContext, IdentityType.UserPrincipalName, userAccountToTest))
{
isAccountLockedOut = (user != null) && user.IsAccountLockedOut();
}
}
Assert.False(isAuthenticated);
Assert.False(isAccountLockedOut);
}
Aquí es la excepción Seguimiento de la pila:
System.DirectoryServices.AccountManagement.PrincipalOperationException : Information about the domain could not be retrieved (1355).
at System.DirectoryServices.AccountManagement.Utils.GetDcName(String computerName, String domainName, String siteName, Int32 flags) at System.DirectoryServices.AccountManagement.ADStoreCtx.LoadDomainInfo() at
System.DirectoryServices.AccountManagement.ADStoreCtx.get_DnsDomainName() at System.DirectoryServices.AccountManagement.ADStoreCtx.GetAsPrincipal(Object storeObject, Object discriminant) at
System.DirectoryServices.AccountManagement.ADStoreCtx.FindPrincipalByIdentRefHelper(Type principalType, String urnScheme, String urnValue, DateTime referenceDate, Boolean useSidHistory) at
System.DirectoryServices.AccountManagement.ADStoreCtx.FindPrincipalByIdentRef(Type principalType, String urnScheme, String urnValue, DateTime referenceDate) at
System.DirectoryServices.AccountManagement.Principal.FindByIdentityWithTypeHelper(PrincipalContext context, Type principalType, Nullable`1 identityType, String identityValue, DateTime refDate) at
System.DirectoryServices.AccountManagement.Principal.FindByIdentityWithType(PrincipalContext context, Type principalType, IdentityType identityType, String identityValue) at
System.DirectoryServices.AccountManagement.UserPrincipal.FindByIdentity(PrincipalContext context, IdentityType identityType, String identityValue)
nadie más ha visto y resuelto este problema? De lo contrario, ¿hay una mejor manera de verificar el estado IsAccountLockedOut
para una cuenta de Active Directory?
Como referencia, todas nuestras máquinas de prueba están dentro de la misma subred. Hay servidores ActiveDirectory separados que ejecutan Windows Server 2003, 2008 y 2012, en una variedad de modos funcionales de dominio (ver a continuación). El código funciona desde máquinas que ejecutan .NET 4.0, pero falla desde máquinas que ejecutan .NET 4.5.
Los tres .NET máquinas que nos encontramos el código de son:
- Windows 7 corriendo .NET 4.0
- Windows Vista que ejecuten .NET 4.5
- Windows Server 2012 que ejecuta .NET 4.5
El Activo servidores de directorio que hemos probado son:
- Windows 2003 con el modo funcional AD dominio establecido en Windows 2000 nativo
- Windows 2003 con el modo funcional AD dominio establecido en Windows Server 2003
- Windows 2008 con el dominio AD Modo funcional s et a Windows 2000 nativo
- Windows 2008 con el modo funcional AD dominio establecido en Windows Server 2003
- Windows 2008 con el modo funcional AD dominio establecido en Windows Server 2008
- Windows 2012 con el modo funcional AD dominio establecido en Windows 2012
Todos los servidores de Active Directory están configurados como un solo bosque simple y las máquinas cliente no son parte del dominio. No se utilizan para ninguna otra función que no sea para probar este comportamiento, y no ejecutan otra cosa que no sea Active Directory.
EDITAR - 9 oct 2012
Gracias a todos los que respondieron. A continuación se muestra un cliente de línea de comandos C# que demuestra el problema, y una solución temporal a corto plazo que identificamos que no requería que cambiáramos nada sobre las configuraciones de Active Directory y DNS. Parece que la excepción solo se lanza una vez con una instancia del PrincipalContext. Incluimos los resultados para una máquina .NET 4.0 (Windows 7) y una máquina .NET 4.5 (Windows Vista).
using System;
using System.DirectoryServices.AccountManagement;
namespace ADBug
{
class Program
{
static void Main(string[] args)
{
const string activeDirectoryServer = "MyActiveDirectoryServer";
const string activeDirectoryLogin = "MyADAccount";
const string activeDirectoryPassword = "MyADAccountPassword";
const string validUserAccount = "[email protected]";
const string unknownUserAccount = "[email protected]";
var principalContext = new PrincipalContext(ContextType.Domain, activeDirectoryServer, activeDirectoryLogin, activeDirectoryPassword);
// .NET 4.0 - First attempt with a valid account finds the user
// .NET 4.5 - First attempt with a valid account fails with a PrincipalOperationException
TestFindByIdentity(principalContext, validUserAccount, "Valid Account - First Attempt");
// Second attempt with a valid account finds the user
TestFindByIdentity(principalContext, validUserAccount, "Valid Account - Second Attempt");
// First attempt with an unknown account does not find the user
TestFindByIdentity(principalContext, unknownUserAccount, "Unknown Account - First Attempt");
// Second attempt with an unknown account does not find the user (testing false positive)
TestFindByIdentity(principalContext, unknownUserAccount, "Unknown Account - Second Attempt");
// Subsequent attempt with a valid account still finds the user
TestFindByIdentity(principalContext, validUserAccount, "Valid Account - Third Attempt");
}
private static void TestFindByIdentity(PrincipalContext principalContext, string userAccountToTest, string message)
{
var exceptionThrown = false;
var userFound = false;
try
{
using (var user = UserPrincipal.FindByIdentity(principalContext, IdentityType.UserPrincipalName, userAccountToTest))
{
userFound = (user != null);
}
}
catch (PrincipalOperationException)
{
exceptionThrown = true;
}
Console.Out.WriteLine(message + " - Exception Thrown = {0}", exceptionThrown);
Console.Out.WriteLine(message + " - User Found = {1}", userAccountToTest, userFound);
}
}
}
.NET 4.0 Salida
Valid Account - First Attempt - Exception Thrown = False
Valid Account - First Attempt - User Found = True
Valid Account - Second Attempt - Exception Thrown = False
Valid Account - Second Attempt - User Found = True
Unknown Account - First Attempt - Exception Thrown = False
Unknown Account - First Attempt - User Found = False
Unknown Account - Second Attempt - Exception Thrown = False
Unknown Account - Second Attempt - User Found = False
Valid Account - Third Attempt - Exception Thrown = False
Valid Account - Third Attempt - User Found = True
.NET 4.5 Salida
Valid Account - First Attempt - Exception Thrown = True
Valid Account - First Attempt - User Found = False
Valid Account - Second Attempt - Exception Thrown = False
Valid Account - Second Attempt - User Found = True
Unknown Account - First Attempt - Exception Thrown = False
Unknown Account - First Attempt - User Found = False
Unknown Account - Second Attempt - Exception Thrown = False
Unknown Account - Second Attempt - User Found = False
Valid Account - Third Attempt - Exception Thrown = False
Valid Account - Third Attempt - User Found = True
No estoy seguro de si ya ha buscado en Google esto, pero encontré una publicación con varios comentarios sobre correcciones para varios escenarios que podría causar esto: http://elegantcode.com/2009/03/21/one-scenario-where-the-systemdirectoryservices-accountmanagement-api-falls-down/ Nunca lo he visto, pero en el interés de ser útil, pensé que lo compartiría ya que me parece útil. – David
por ejemplo, ha intentado pasar en DC y CN, por ejemplo PrincipalContext ctx = new PrincipalContext ( ContextType.Domain, "fabrikam.com", "CN = Usuarios, DC = fabrikam, DC = com", "administrator" , "securelyStoredPassword"); – MethodMan
DJ, simplemente tratamos de pasar la cadena CN y DC, y todavía funciona bajo .NET 4.0 y falla bajo .NET 4.5. –