Interagindo com AD usando Código C#

Este tópico mostra como interagir com o Active Directory do Windows através dos Comandos C# do Formulário Dinâmico.

Banco de Dados

Para iniciar, vamos criar uma tabela para gravar os usuários do AD em nosso banco de dados:

CREATE TABLE adusers
(
   username VARCHAR(60) NOT NULL,                                                
   commonname  VARCHAR(255),
   displayname VARCHAR(255),
   email       VARCHAR(100),
   CONSTRAINT pk_adusers_usrname PRIMARY KEY (username)
);

Obtendo Usuários

A rotina baixo pode ser chamada em qualquer procedimento do Formulário Dinâmico.
Para isso, clique no botão direito do mouse sobre o procedimento, vá até o menu Ação → Executar Comando C#

O código C# abaixo usa a biblioteca System.DirectoryServices do .NET Framework para acessar as informações do AD. Como essa biblioteca não está referenciada no projeto, ela é chamada via Reflection, usando o método LoadAssembly do SDK do Latromi para carregar a DLL e o método Activator.CreateInstance(...) do .NET para instanciar as classes DirectoryEntry e DirectorySearcher.

Os campos que serão gravados são:

  • Nome de Usuário :key:
  • Nome Comum
  • Nome de Exibição
  • E-mail

O código faz primeiro um UPDATE. Se nenhum registro foi afetado, então faz um INSERT.

using System.Reflection;
using System.Collections;
using LATROMI.Extensions;
using System.Data;

// Informar os dados do usuário para autenticar no AD e realizar as operações.
// Preferencialmente um administrador.
string adUser = "usuario_administrador";
string adUserPassword = "senha_do_usuario";
string adUserDomain = "dominio";
// Fully qualified domain name ou FQDN é o nome de domínio que especifica a posição do nó na hierarquia do Domain Name System (DNS). 
// Um FQDN é estruturado da seguinte forma: "host.3rd-level-domain.2nd-level-domain.top-level-domain". O número de níveis em um FQDN não é fixo
string adFQDN = "dominio.com.br";

string adUserAndDomain = string.Concat(adUserDomain, @"\", adUser);
string ldapAddress = "LDAP://" + adFQDN;

// Validações
if (string.IsNullOrEmpty(adUser)) throw new InvalidOperationException("Nome de usuário não informado.");
if (string.IsNullOrEmpty(adUserPassword)) throw new InvalidOperationException("Senha de usuário não informada.");
if (string.IsNullOrEmpty(adUserDomain)) throw new InvalidOperationException("Domínio de usuário não informado.");
if (string.IsNullOrEmpty(adFQDN)) throw new InvalidOperationException("FQDN não informado.");

var directoryServicesAssembly = LoadAssembly("System.DirectoryServices.dll");
var directoryEntryType = directoryServicesAssembly.GetType("System.DirectoryServices.DirectoryEntry");
var directorySearchType  = directoryServicesAssembly.GetType("System.DirectoryServices.DirectorySearcher");

dynamic directoryEntry = Activator.CreateInstance(directoryEntryType, ldapAddress, adUserAndDomain, adUserPassword);

//Carrega o objeto nativo do AD para forçar a autenticação.    
var obj = directoryEntry.NativeObject;

dynamic search = Activator.CreateInstance(directorySearchType, directoryEntry);

search.Filter = "(&(objectClass=user)(objectCategory=person))";
search.PageSize = 100;

var properties = (IList)search.PropertiesToLoad;

// Seleciona as propriedades que serão carregadas
//
properties.Add("cn"); //Common Name
properties.Add("samaccountname");
properties.Add("usergroup");
properties.Add("displayname");//first name
properties.Add("userPrincipalName");
properties.Add("memberOf"); // groups
properties.Add("userAccountControl");
//
// Informações referentes às funções do usuário.
properties.Add("title");
properties.Add("department");
properties.Add("company");
properties.Add("manager");
//
// Informações referente aos contatos do usuário.
properties.Add("telephoneNumber");
properties.Add("mail");
//
// Informações referentes ao endereço do usuário.
properties.Add("streetAddress");
properties.Add("l"); // City
properties.Add("st"); // State
properties.Add("postalCode");
properties.Add("co"); //Country

var searchResultCollection = search.FindAll();

if (searchResultCollection != null)
{
    // Abre a conexão 
    using (var connection = Database.CreateConnection("Teste AD"))
    {
        // Abre a Conexão
        connection.Open();
        
        foreach(object searchResult in searchResultCollection)
        {
            dynamic resultObj = searchResult;
            
            // Obtém os dados
            string username = (string)resultObj.Properties["samaccountname"][0];
            string commonName = (string)resultObj.Properties["cn"][0];
            // Display Name e Email podem não estar presentes no AD, 
            // portanto é importante verificar se existe para evitar erros
            string displayName = resultObj.Properties.Contains("displayname") == true 
                        ? (string)resultObj.Properties["displayname"][0]
                        : null;
                //
            string email = resultObj.Properties.Contains("mail") == true
                        ? (string)resultObj.Properties["mail"][0]
                        : null;
            
            // Salva no banco de dados
            var commandParameters = new Dictionary<string, object> 
            {
                ["@username"] = username.ToUpper(),
                ["@commonname"] = commonName,
                ["@displayname"] = displayName ?? Convert.DBNull,
                ["@email"] = email ?? Convert.DBNull
            };
            
            int affectedRows = connection.ExecuteNonQuery( 
                "UPDATE adusers SET "+
                " commonname = @commonname " +
                " , displayname = @displayname " +
                " , email = @email " +
                " WHERE username = @username", commandParameters);
                
            // Se nenhuma linha foi afetada pelo UPDATE,
            // então faz o INSERT do usuario
            if (affectedRows <= 0) 
            {
                connection.ExecuteNonQuery(
                    "INSERT INTO adusers (username, commonname, displayname, email) " +
                    "VALUES (@username, @commonname, @displayname, @email)", commandParameters);
            }
        }
    }
}

Obtendo um usuário Específico

Para listar todos os usuários, o filtro usando na classe DirectorySearcher é este:

search.Filter = "(&(objectClass=user)(objectCategory=person))";

Para obter os dados de um usuário específico, sem precisar percorrer todos os outros usuários, basta substituir o filtro por:

search.Filter = "(SAMAccountName=" + "nome_de_usuario" + ")";