Accediendo al directorio activo de la organización desde .NET (IV)

homer1Buscar

Hoy vamos a buscar. Buscar elementos en el AD dentro de nuestra organización, y como lo más habitual es buscar usuarios o grupos he creado algunas funciones para facilitar esta tarea dentro de la clase LDAPServices (os dejo para vosotros ampliarlas para buscar equipos, por ejemplo).

También veremos cómo extraer los nombres de las propiedades de un objetos del AD, ya que en ocasiones queremos filtrar o devolver el valor de una propiedad de un objeto y no sabemos cómo se llama esta propiedad. Por ejemplo, para devolver el teléfono de un usuario en el AD hay que preguntar por el valor de la propiedad ‘telephoneNumber’.

Tal vez más adelante (si tengo tiempo) lo ampliemos un poco. Me gustaría realizar un post acerca de cómo realizar un mapeador de propiedades para asignar los valores de las propiedades de objetos LDAP a objetos de nuestra aplicación. Esto podría ser muy útil por ejemplo, para importar los valores de nuestros usuarios de AD a una tabla de empleados.

Antes de empezar quiero comentaros que para poder realizar todo esto va a ser necesario agregar una referencia a System.DirectoryServices a nuestro proyecto. Pero vamos ya con estos nuevos métodos de LDAPServices:

getLDAPFilterString – Devuelve una cadena de consulta en formato LDAP query, que permite filtrar los objetos que deseamos devolver. Esta función filtra aquellos carácteres no deseados para evitar posible inyección de código LDAP por parte de un usuario (aunque es muy mejorable, estoy seguro que mi JoseMariCariño sería capaz de sacar información de aquí, casi me apuesto algo).

public enum LDAPFilterType
{
    UsersAndGroups,
    OnlyUsers,
    OnlyGroups
}
 
public static string 
    getLDAPFilterString(LDAPFilterType Type, string Filter)
{
    Filter = Filter.Replace("&","");
    Filter = Filter.Replace("|","");
    Filter = Filter.Replace("*", "");
    string FilterByName = "(samAccountName=*{0}*)";
    string f = string.Empty;
    switch (Type)
    {
        case LDAPFilterType.OnlyUsers:
            f = "(&(objectCategory=person)(objectClass=user){0})";
            break;
        case LDAPFilterType.OnlyGroups:
            f = "(&(objectCategory=Group){0})";
            break;
        case LDAPFilterType.UsersAndGroups:
            f = "(|(&(objectCategory=person)(objectClass=user){0})(&(objectCategory=Group){0}))";
            break;
    }
    if (Filter == string.Empty)
    {
        return string.Format(f, string.Empty);
    }
    else
    {
        return string.Format(f, string.Format(FilterByName, Filter));
    }
} 

getItemsInLDAP – Basándose en el método anterior, realiza la consulta al AD y devuelve una lista con los objetos coincidentes. Su funcionamiento se basa en un objeto DirectoryEntry, que apunta al AD que se le ha pasado como aergumento. Y en un objeto DirectorySearcher, que es el que realmente se encarga de buscar las entradas coincidentes con el filtro en el DirectoryEntry.

public static List<string> 
    getItemsInLDAP(string LDAPURL, LDAPFilterType type, string criteria)
{
    List<string> items = new List<string>();
    DirectoryEntry entries = new DirectoryEntry(LDAPURL);
    string filter = getLDAPFilterString(type, criteria);
    DirectorySearcher searcher = new DirectorySearcher(
        entries, filter);
    try
    {
        foreach (SearchResult result in searcher.FindAll())
        {
            items.Add((string)result.Properties["samAccountName"][0]);
        }
    }
    catch (Exception ex)
    {
        throw ex;
    }
    return items;
}

Para probar esta funcionalidad basta con tener un TextBox en el que introducir el criterio de búsqueda y un ComboBox en el que especificar que objetos deseamos buscar. Así cómo un CommandButon para lanzar la consulta y un ListBox en el que mostrar los resultados:

LDAPSearchInLDAP

El código es muy sencillo. Basta con asignar el valor devuelto por la función getItemsInLDAP al DataSource de la lista.

private void cmbSearch_Click(object sender, EventArgs e)
{
    try
    {
        string dcName = LDAPServices.getLDAPDomainName(txtDomain.Text);
        List<string> items = LDAPServices.getItemsInLDAP(dcName, 
            (LDAPServices.LDAPFilterType) cmbType.SelectedIndex, txtCriteria.Text);
        lstItems.DataSource = items;
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message,
             Application.ProductName,
             MessageBoxButtons.OK,
             MessageBoxIcon.Exclamation);
    }
}

getUserLDAPProperties – Devuelve una lista de cadenas con los nombres de las propiedades de un objeto User dentro del AD. Cabe observar que el esquema del AD es variable, con lo que las propiedades devueltas pueden cambiar. Por ejemplo, productos que se integran fuertemente con AD como Exchange agregan propiedades a los objetos usuario y grupo.

public static List<string> 
    getUserLDAPProperties(string LDAPURL)
{
    List<string> properties =new List<string>();
    DirectoryEntry entries = new DirectoryEntry(LDAPURL);
    DirectorySearcher searcher = new DirectorySearcher(
        entries, "(&(objectCategory=person)(objectClass=user))");
    try
    {
        foreach (SearchResult result in searcher.FindAll())
        {
            foreach (string property in
                result.GetDirectoryEntry().Properties.PropertyNames)
            {
                properties.Add(property);
            }
            break;
        }
    }
    catch (Exception ex)
    {
        throw ex;
    }
    return properties;
}

LDAPBrowseUserProperties

De este modo podemos saber las propiedades de un objeto dentro de nuestro AD. Esto es más importante de lo que parece, ya que cuando buscamos elementos dentro del AD, no se devuelven todas las propiedades de forma predeterminada. Para agregar el valor de una propiedad a los resultados de la búsqueda ésta debe añadirse explícitamente mediante la colección PropertiesToLoad:

searcher.PropertiesToLoad.Add("telephoneNumber");

Y luego comprobar si existe valor devuelto, ya que es posible que no exista o no devuelva valor:

if (r.Properties("telephoneNumber").Count > 0)
{
    //
}

Y hasta aquí el cuarto capítulo de esta serie. Espero que os sea útil, recordar que en esta ocasión el código completo lo publiqué en el post anterior.

Nos vemos pronto!
Un saludo desde Andorra,

** crossposting desde el blog de Lluís Franco en geeks.ms **

One thought on “Accediendo al directorio activo de la organización desde .NET (IV)

  1. hola doc, mi estimado he leido tus codigo y son muy bueno e incluso me he guiado de ellos para realizar un aplicativo en .net2005 en donde le mando el nombre (full name) de un usuario y quiero que me devuelva el codigo que le he asignado. El aplicativo funciona PERO una cierta cantidad de usuarios no me devuelve codigo.
    –este es el metodo:
    Public Function FullName(ByVal domain As String, ByVal username As String, ByVal valor As String) As ArrayList
    Dim _path As String = “”
    valor = “sAMAccountName”
    Dim enTry As DirectoryEntry = New DirectoryEntry(_path)
    Try
    ‘ Bind to the native AdsObject to force authentication.
    Dim search As DirectorySearcher = New DirectorySearcher(enTry)
    search.Filter = “(cn=” + username + “)”
    search.PropertiesToLoad.Add(valor)
    Dim result As SearchResult = search.FindOne()
    If Nothing Is result Or result Is “” Then
    ‘MessageBox.Show(“Usuario no Existe”)
    End If
    ‘ Update the new path to the user in the directory
    valor = “samAccountName”
    _path = result.Path
    If valor.Equals(“samAccountName”) Then
    _filterAttribute.Add(CType(result.Properties(valor)(0), String))

    Else
    ‘MessageBox.Show(“El Usuario no Existe o no Tiene Correo”)
    End If

    Catch ex As Exception
    ‘MessageBox.Show(“El Usuario no Existe o no Tiene Correo”)
    End Try
    Return _filterAttribute
    end Function

    aun no le saco bien el patron del error ya que mis ous estan de la siguiente manera:
    OU=Usuarios,OU=OU Peru, DC=abc, DC=COM, DC=PE y dentro de usuarios tengo sub OU´s llamados OU=Genericos, OU=personal Externo, OU=Personal Gualtemala, OU=personal Planilla, OU=personal Practicantes.

    ojala me puedas ayudar.
    gracias de antemano.
    mi correo es i410222@cibertec.edu.pe

Leave a Reply

Your email address will not be published. Required fields are marked *