Bug do .NET 4.5 em UserPrincipal.FindByIdentity (System.DirectoryServices.AccountManagement)
Ao testar nosso aplicativo .NET 4.0 no .NET 4.5, encontramos um problema com oFindByIdentity
método paraUserPrincipal
. O código a seguir funciona quando executado em um tempo de execução do .NET 4.0, mas falha no .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);
}
Aqui está o rastreamento da pilha de exceção:
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)
Alguém viu e resolveu este problema? Se não, existe uma maneira melhor para verificarmos oIsAccountLockedOut
status para uma conta do Active Directory?
Para referência, todas as nossas máquinas de teste estão dentro da mesma sub-rede. Existem servidores ActiveDirectory separados que executam o Windows Server 2003, 2008 e 2012, em vários modos funcionais de domínio (veja abaixo). O código funciona em máquinas que executam o .NET 4.0, mas falha em máquinas que executam o .NET 4.5.
As três máquinas .NET das quais rodamos o código são:
- Windows 7 executando o .NET 4.0
- Windows Vista executando o .NET 4.5
- Windows Server 2012 executando o .NET 4.5
Os servidores do Active Directory que experimentamos são:
- Windows 2003 com o Modo Funcional de Domínio AD definido para o nativo do Windows 2000
- Windows 2003 com o Modo Funcional do Domínio AD definido para o Windows Server 2003
- Windows 2008 com o Modo Funcional de Domínio AD definido para o nativo do Windows 2000
- Windows 2008 com o Modo Funcional do Domínio AD definido para o Windows Server 2003
- Windows 2008 com o Modo Funcional de Domínio AD definido para o Windows Server 2008
- Windows 2012 com o Modo Funcional do Domínio AD definido para o Windows 2012
Todos esses servidores do Active Directory são configurados como uma floresta simples e única, e as máquinas clientes não fazem parte do domínio. Eles não são usados para nenhuma outra função além de testar esse comportamento e não estão executando nada além do Active Directory.
EDIT - 9 de outubro de 2012
Obrigado a todos que responderam. Abaixo está um cliente de linha de comando em C # que demonstra o problema e uma solução temporária de curto prazo que identificamos que não exigia que alterássemos nada sobre as configurações do Active Directory e do DNS. Parece que a exceção só é lançada uma vez com uma instância do PrincipalContext. Incluímos as saídas para uma máquina .NET 4.0 (Windows 7) e uma 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);
}
}
}
Saída do .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
Saída do .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