Archivo

Archive for 23 abril 2012

An operations error occurred con Directorio Activo


Si me has seguido últimamente sabrás que he estado trabajando con el Directorio Activo. Primero escribí sobre cómo realizar búsquedas de usuarios en el AD. Luego, hablé sobre un problema de acceso denegado al actualizar propiedades del AD. Pues bien, ahora nos hemos topado con otro problema.

Resulta que al ejecutar el código mostrado en el artículo anterior todo funciona bien. Hasta que metimos dicho código en una página de aplicación de SharePoint. Cuál no sería nuestra sorpresa al encontrarnos con un System.DirectoryServices.DirectoryServicesCOMException con el siguiente mensaje de error: “an operations error occurred”. Sin mayor información.

La frustración fue mayor cuando vimos que el error ocurría al realizar la búsqueda de usuario, ni siquiera al actualizar algún dato.

auto searcher = gcnew DirectorySearcher();            
searcher->Filter = String::Format(L"(SAMAccountName={0})", account);            
            
auto result = searcher->FindOne(); // <-- An operations error occurred

Pues menudo lío. Comenzamos a hacer una búsqueda en Internet, con varias recomendaciones. En particular dimos con esta: Common System.DirectoryServices Issues and Solution. Intentamos seguir las recomendaciones expuestas en la tercera pregunta, con estos resultados.

1.- Usar acceso anónimo. No podemos porque todo el portal de SharePoint no usa acceso anónimo, y por reglas de seguridad del cliente no podemos emplearlo.

2.- Utilizar un componente COM+. ¿En serio? ¿Quién emplea COM+ hoy en día? Además instalar este tipo de componentes nos hubiera llevado días, por políticas del cliente.

3.- Delegación mediante <identity impersonate=”true”> en el web.config. Lo intentamos, pero no nos funcionó.

4.- Correr el proceso del IIS como usuario de dominio. El cliente casi nos corre de su oficina. Y no podemos usar al usuario que ya nos había dado porque pondría en riesgo toda la granja ya existente.

5.- Personificar programáticamente al usuario. Lo intentamos, pero ¿cómo?

Es decir, 5) parecía el camino a seguir. Intentamos correr SPSecurity.RunWithElevatedPrivileges sin suerte. El problema es que el usuario y la contraseña los ponemos después de encontrar al usuario, cuando queremos modificar. ¿Entonces?

DirectorySearcher tiene un parámetro en su constructor: un DirectoryEntry (al cual podríamos poner un usuario y una contraseña), el cual usualmente tiene la unidad organizacional de la raíz del dominio. El problema en mi caso es que mi cliente tiene un bosque con ocho dominios, y ciertamente quiere que buscásemos en cada uno de estos al usuario, porque cambian constantemente. De ahí que DirectorySearcher siempre lo iniciábamos con el constructor vacío.

Fue ahí cuando se me ocurrió: ¿y si creo un DirectoryEntry vacío, le pongo un usuario y contraseña, y lo paso al DirectorySearcher?

auto entry = gcnew DirectoryEntry();
entry->Username = "arthur.dent";
entry->Password = "42";

auto searcher = gcnew DirectorySearcher(entry);
searcher->Filter = String::Format(L"(SAMAccountName={0})", account);

auto result = searcher->FindOne();

 

Y para mi sorpresa, el código anterior funcionó. Nota que el DirectoryEntry, en lugar de pasarle el dominio o alguna OU, lo dejamos vacío y sólo le pusimos las credenciales. Luego, creamos el searcher con éstas, y buscamos al usuario. Y vaya: nos trajo todo completito. Y ahora ya está funcionando esta parte.

¡Espero que esto sirva de ayuda!

Anuncios

Acceso denegado al actualizar propiedades del directorio activo


Hace poco publiqué una entrada sobre un pequeño código para leer un usuario en un directorio activo. También comentaba que estuve apoyando a un colega con esto. Continuando con la historia, resulta que estuvimos trabajando para no solo leer el código, sino para modificarlo. Para hacer eso, utilizamos un código similar al siguiente.

try
{
    auto searcher = gcnew DirectorySearcher();            
    searcher->Filter = String::Format(L"(SAMAccountName={0})", account);
    searcher->PropertiesToLoad->Add(L"displayName");
    searcher->PropertiesToLoad->Add(L"givenname");
    searcher->PropertiesToLoad->Add(L"sn");
    searcher->PropertiesToLoad->Add(L"company");
            
    auto result = searcher->FindOne();
    if (result == nullptr)
        throw gcnew Exception(L"Usuario no encontrado.");

    auto entry = result->GetDirectoryEntry();
    entry->Properties[L"displayName"]->Value = L"Arthur Dent";
    entry->Properties[L"givenname"]->Value = L"Arthur";
    entry->Properties[L"sn"]->Value = L"Dent";
    entry->Properties[L"company"]->Value = L"Heart of Gold";
    entry->CommitChanges();
}
catch (Exception^ ex)
{
    Console::WriteLine(ex->Message);
}

En este código buscamos un usuario por su SAM AccountName, cargamos algunas propiedades y luego las modificamos. Queremos buscar a un usuario y asignarle sus nombres y la compañía en la que trabaja. Cualquiera que haya leído la documentación de MSDN puede llegar a un código como el anterior: la colección “Properties” contiene las propiedades en el directorio activo. Y el método CommitChanges hace la actualización hacia el AD.

Bien. Hasta aquí pensábamos que la vida era rosa. Ejecutamos el código y ¡BOOM! excepción: SecurityException, Access Denied. Que no panda el cúnico, nos dijimos. Seguramente es de esperar que no cualquier fulano pueda modificar al AD. Entonces probamos ejecutar la anterior aplicación con permisos de administrador.

Nada. Bueno, quizás el administrador que utilizábamos no tiene permisos de modificar el AD. Le hablamos a nuestro cliente y le pedimos que nos generara un usuario con permisos para modificar el dichoso AD.

 

try
{
    auto searcher = gcnew DirectorySearcher();            
    searcher->Filter = String::Format(L"(SAMAccountName={0})", account);
    searcher->PropertiesToLoad->Add(L"displayName");
    searcher->PropertiesToLoad->Add(L"givenname");
    searcher->PropertiesToLoad->Add(L"sn");
    searcher->PropertiesToLoad->Add(L"company");
            
    auto result = searcher->FindOne();
    if (result == nullptr)
        throw gcnew Exception(L"Usuario no encontrado.");

    auto entry = result->GetDirectoryEntry();
    entry->Username = L"my.admin";
    entry->Password = L"42";
    entry->Properties[L"displayName"]->Value = L"Arthur Dent";
    entry->Properties[L"givenname"]->Value = L"Arthur";
    entry->Properties[L"sn"]->Value = L"Dent";
    entry->Properties[L"company"]->Value = L"Heart of Gold";
    entry->CommitChanges();
}
catch (Exception^ ex)
{
    Console::WriteLine(ex->Message);
}

Como puedes ver, el código cambió un poco. Las líneas resaltadas muestran cómo pusimos el nombre de usuario y la contrasña del usuario que nos generó el cliente, con permisos para modificar el AD. Corremos el programa, y… ¡tan tan taaan! Acceso denegado.

Estuvimos explorando varias posibilidades. Hablamos con el cliente, le dijimos que seguía dando el error. Le solicitamos que pusiera al usuario que nos generó dentro del grupo de Enterprise Administratos. A lo que se negó, por supuesto. Nos explicó que lo que hizo fue delegar los permisos para que pudiera modificar propiedades, y que ya se había propagado el cambio en todo el bosque.

Frustrados, intentamos buscar más explicaciones. Hasta que en una reunión con la gente de AD del cliente, se pusieron a revisar y se dieron cuenta de que los usuarios que intentábamos modificar tenían la opción marcada para que no se replicara la delegación de permisos. Jope: eran las cuentas destino las que no podían modificarse. Bastó unos cuantos minutos para que cambiaran en el AD a esas cuentas, y todo funcionó a la perfección.

Así que, en resumen, si obtienes un acceso denegado, revisa:

1.- Impersonar con una cuenta que sí tenga permisos para modificar el AD.

2.- Que las cuentas destino tengan permisos para ser modificadas.

3.- En caso de que deleguen permisos, que las cuentas destino acepten la propagación de delegación.

4.- Si no funciona lo anterior, que el usuario ingrese al grupo de Enterprise Administrators y búscale a partir de ahí.

¡Saludos!

Reader Q&A: When will better JITs save managed code?


Un interesante artículo de Herb Sutter, en el cual nos adelanta por qué los lenguajes administrados nunca serán tan rápidos como los nativos –particularmente .NET y Java vs C++. ¡Y no es cuestión del compilador JIT!

Reader Q&A: When will better JITs save managed code?.

Espero que pronto llegue el blog con mayor explicación.

Categorías:C++, Noticias Etiquetas: , ,