miércoles, 4 de marzo de 2009

Impersonar Temporalmente

¿Alguna vez les ha pasado que están desarrollando un web service y requieren acceder a una carpeta compartida? ¿No? pues a mi me pasó. Necesitaba dejar un archivo en una carpeta compartida que de paso ya tenia un usuario y una contraseña particular para este tipo de tareas, por lo que no queda mas remedio de impersonar temporalmente.

¿Como? haciendo una clase sobrepuesta a las dlls de Windows que se requieren para tal propósito. Siguiendo este articulo se puede crear una clase similar a la siguiente que sirva a nuestros intereses:


using System;
using System.Runtime.InteropServices;
using System.Security.Principal;


namespace Foyland.Comun
{
//Esta clase impersonaliza un usuario temporalmente
public class Impersonalizacion
{
enum LogonSessionType : uint
{
Interactive = 2, //Esta tiene permisos sobre los recursos de red
Network, //Esta No tiene permisos sobre los recursos de red (curioso no?)
Batch,
Service,
NetworkCleartext = 8,
NewCredentials
}

enum LogonProvider : uint
{
Default = 0, // default (usar esta)
WinNT35, // usa una señales dummy para autenticar (sends smoke signals to authority)
WinNT40, // usa NTLM
WinNT50 // usa Kerberos o NTLM
}

[DllImport("advapi32.dll", SetLastError = true)]
static extern bool LogonUser(
string principal,
string authority,
string password,
LogonSessionType logonType,
LogonProvider logonProvider,
out IntPtr token);

[DllImport("advapi32.dll", EntryPoint = "DuplicateToken", ExactSpelling = false,
CharSet = CharSet.Auto, SetLastError = true)]

public static extern int DuplicateToken(
IntPtr ExistingTokenHandle,
int ImpersonationLevel,
ref IntPtr DuplicateTokenHandle);

public static WindowsImpersonationContext WinLogOn(
string strUsuario,
string strClave,
string strDominio)
{
IntPtr tokenDuplicate = new IntPtr(0);
IntPtr tokenHandle = new IntPtr(0);
if (LogonUser(strUsuario, strDominio, strClave, LogonSessionType.Interactive,
LogonProvider.Default, out tokenHandle))

if (DuplicateToken(tokenHandle, 2, ref tokenDuplicate) != 0)
return (new WindowsIdentity(tokenDuplicate)).Impersonate();
return null;

}
}
}

Aqui termina la case que necesitamos para impersonar. Ahora necesitamos invocarla desde el webservice. Porción de codigo de ejemplo:

using System.Configuration;
using System.IO;
using System.Security.Principal;
.
.
.
//ya dentro del metodo apropiado

//Carga las variables que estan en el web.config
string usuario = ConfigurationManager.AppSettings.Get("USUARIO_DFS");
string password = ConfigurationManager.AppSettings.Get("PASSWORD_DFS");
string dominio = ConfigurationManager.AppSettings.Get("DOMINIO_DFS");

//crea el objeto necesario para impersonalizar
WindowsImpersonationContext _objContext = null;

//Despues de esta sentencia ya estamos impersonalizando con el usuario deseado
_objContext = Impersonalizacion.WinLogOn(usuario, password, dominio);
try
{
//Codigo para crear y guardar el archivo en la carperta compartida;
}
catch (Exception ex)
{
throw ex;
}
finally
{
//SIEMPRE debemos ejecutar el metodo Undo para regresar a las credenciales anteriores
_objContext.Undo();
}

Para lo que necesitaba funciona perfectamente.

4 comentarios: