c# - Error.NET 4.5 en UserPrincipal.FindByIdentity(System.DirectoryServices.AccountManagement)
authentication active-directory (3)
Estamos experimentando exactamente el mismo problema (las consultas entre dominios no se actualizan a 4.5) - Considero que esto es un error ya que rompe el código existente (4.0).
Sin embargo, con el interés de hacerlo funcionar, echando un vistazo a uno de los (ahora) clientes que fallaban, noté que había un montón de solicitudes DNS para registros SRV que fallaban, de la forma:
_ldap._tcp.MYSERVER1.mydomain.com,INet,Srv
_ldap._tcp.dc._msdcs.mydomain.com,INet,Srv
La modificación de nuestro servidor DNS (el DNS utilizado por los clientes con errores) para tener una zona de reenvío para todo el tráfico de midominio.com a uno de los DC en el dominio resolvió el problema.
Usando nslookup, el comportamiento desde antes (cuando estaba fallando) hasta ahora (funcionando) era que antes esas consultas devolvían "dominio inexistente" mientras que ahora devuelven " * No hay registros de ubicación de servicio (SRV) disponibles para ..." . El punto de falla parece ser la inexistencia percibida del dominio en lugar de registros SRV perdidos. Es de esperar que MS revierte este comportamiento, pero mientras tanto, puede tener algo de suerte al crear una zona de reenvío de DNS si puede controlar el DNS para los clientes que fallan.
Al probar nuestra aplicación .NET 4.0 bajo .NET 4.5, hemos encontrado un problema con el método UserPrincipal
para UserPrincipal
. El siguiente código funciona cuando se ejecuta en un tiempo de ejecución de .NET 4.0, pero falla en .NET 4.5:
[Test]
public void TestIsAccountLockedOut()
{
const string activeDirectoryServer = "MyActiveDirectoryServer";
const string activeDirectoryLogin = "MyADAccount@MyDomain";
const string activeDirectoryPassword = "MyADAccountPassword";
const string userAccountToTest = "TestUser@MyDomain";
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í está el rastro de pila de excepción:
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)
¿Alguien más ha visto y resuelto este problema? De lo contrario, ¿hay una mejor manera de verificar el estado de 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.
Las tres máquinas .NET con las que ejecutamos el código son:
- Windows 7 ejecutando .NET 4.0
- Windows Vista ejecutando .NET 4.5
- Windows Server 2012 ejecutando .NET 4.5
Los servidores de Active Directory que hemos probado son:
- Windows 2003 con modo funcional AD Domain configurado en Windows 2000 nativo
- Windows 2003 con modo funcional de dominio AD configurado en Windows Server 2003
- Windows 2008 con el modo funcional AD Domain configurado en Windows 2000 nativo
- Windows 2008 con modo funcional de dominio AD configurado en Windows Server 2003
- Windows 2008 con modo funcional de dominio AD configurado en Windows Server 2008
- Windows 2012 con el modo funcional del dominio AD configurado en Windows 2012
Todos esos servidores de Active Directory están configurados como un bosque sencillo y único, 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 de octubre de 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);
}
}
}
Salida de .NET 4.0
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
Salida .NET 4.5
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
Para el OP (y para cualquier otra persona que haya ayudado con las respuestas) hemos tenido el mismo problema exacto. En nuestro entorno de desarrollo, VS2012 instalado y nuestra aplicación se rompió en el tiempo de ejecución durante el inicio de sesión (problema AD como se señaló anteriormente). Así que hice que mi sistema se borrara y continué usando 2010, mientras derramaba lágrimas cada vez que leía una nueva entrada de blog sobre lo increíble 2012 es bla, bla.
Así que encontré este hilo gracias a Scott Hanselman. Instalé una VM en mi caja de desarrollo, Windows 8 developer 90day preview en ella, y VS2012. Puso en marcha nuestra aplicación e inmediatamente recibió el golpe AD de inicio de sesión. Simplemente envolvió nuestro FindByIdentity en una captura de prueba y lo obligó a intentar nuevamente después del primer catch - ¡y viola funciona! Entonces, ¡gracias a quien sea que haya imaginado ese pequeño truco!
Por lo tanto, es una solución menor, y un "truco" que funciona para el desarrollo local, y no debería afectar la producción ya que no estamos poniendo 4.5 en producción en el corto plazo.
Pero la desventaja es que a nivel local, iniciar sesión ahora lleva como 2 minutos en comparación con los segundos cuando funcionamos en 2010 :(
Realmente no sé qué más puedo ofrecer para tratar de ayudar a resolver la situación, pero pensé que compartiría mis 2 centavos de todos modos, ya que esto todavía parece ser un problema importante.
Tuve el mismo problema después de actualizar .NET Framework de 4.0 a 4.5. He ugpraded framework to .net 4.5.1 y funcionó.