Después de buscar por Internet encontré la forma de hacerlo en Visual Basic .Net en la página de CodeProject. Puedes consultar la fuente original aquí (te animo hacerlo que el mérito aquí lo tiene Alex B. Clarke).
Os cuelgo el código de la clase que realiza "el milagro" para que os sea fácil reutilizarla en cualquier solución .Net. Como veréis, su uso es muy sencillo: simplemente tenéis que llamar al método ImpersonateUser() de la clase Impersonate desde vuestra aplicación de escritorio y a partir de esa llamada vuestra aplicación estará utilizando las credenciales del usuario que hayáis especificado.
Para que la aplicación se vuelva a ejecutar con los permisos del usuario que ha ejecutado el programa, basta con llamar a la función Undo().
Tened presente que el usuario utilizado en la impersonalización puede que no tenga permisos de acceso a los recursos de la máquina en los que se está ejecutando la aplicación, con lo que podéis tener ciertas restricciones en este sentido (en mi caso por ejemplo la aplicación en cuestión accede a un directorio de un servidor con unas credenciales diferentes a las que iniciado sesión en la máquina, accede a un PDF y lo visualiza por pantalla; pues bien, la visualización en PDF no funcionaba si no deshacía antes la impersonalización).
Y ahora el código:
using
System;
using
System.Drawing;
using
System.Collections;
using
System.ComponentModel;
using
System.Windows.Forms;
using
System.Data;
using
System.Runtime.InteropServices; // DllImport
using
System.Security.Principal; //
WindowsImpersonationContext
using
System.Security.Permissions; //
PermissionSetAttribute
using
System.IO;
namespace
ImpersonateLib
{
// group type
enum
public enum SECURITY_IMPERSONATION_LEVEL
: int
{
SecurityAnonymous = 0,
SecurityIdentification = 1,
SecurityImpersonation = 2,
SecurityDelegation = 3
}
/// <summary>
/// Summary description for Form1.
/// </summary>
public static class Impersonate
{
// obtains
user token
[DllImport("advapi32.dll", SetLastError = true)]
private
static extern bool LogonUser(string
pszUsername, string pszDomain, string pszPassword, int
dwLogonType, int dwLogonProvider, ref IntPtr
phToken);
// closes
open handes returned by LogonUser
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
private
extern static bool CloseHandle(IntPtr
handle);
// creates
duplicate token handle
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private
extern static bool DuplicateToken(IntPtr
ExistingTokenHandle, int
SECURITY_IMPERSONATION_LEVEL, ref IntPtr DuplicateTokenHandle);
/// <summary>
/// Attempts to impersonate a user. If successful, returns
/// a WindowsImpersonationContext of the new users identity.
/// </summary>
/// <param
name="sUsername">Username you
want to impersonate</param>
/// <param
name="sDomain">Logon domain</param>
/// <param
name="sPassword">User's password
to logon with</param></param>
/// <returns></returns>
public static WindowsImpersonationContext
ImpersonateUser(string sUsername, string sDomain, string
sPassword)
{
//
initialize tokens
IntPtr
pExistingTokenHandle = new IntPtr(0);
IntPtr
pDuplicateTokenHandle = new IntPtr(0);
pExistingTokenHandle = IntPtr.Zero;
pDuplicateTokenHandle = IntPtr.Zero;
// if
domain name was blank, assume local machine
if
(sDomain == "")
sDomain = System.Environment.MachineName;
try
{
string
sResult = null;
const
int LOGON32_PROVIDER_DEFAULT = 0;
//
create token
const
int LOGON32_LOGON_INTERACTIVE = 2;
//
get handle to token
bool
bImpersonated = LogonUser(sUsername, sDomain, sPassword,
LOGON32_LOGON_INTERACTIVE,
LOGON32_PROVIDER_DEFAULT, ref
pExistingTokenHandle);
// did
impersonation fail?
if
(false == bImpersonated)
{
int
nErrorCode = Marshal.GetLastWin32Error();
sResult = "LogonUser() failed with error code: " +
nErrorCode + "\r\n";
// show the
reason why LogonUser failed
throw
new Exception(sResult);
}
//
Get identity before impersonation
sResult += "Before impersonation: " + WindowsIdentity.GetCurrent().Name + "\r\n";
bool
bRetVal = DuplicateToken(pExistingTokenHandle, (int)SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation,
ref pDuplicateTokenHandle);
//
did DuplicateToken fail?
if
(false == bRetVal)
{
int
nErrorCode = Marshal.GetLastWin32Error();
CloseHandle(pExistingTokenHandle); // close
existing handle
sResult += "DuplicateToken() failed with error code: "
+ nErrorCode + "\r\n";
//
show the reason why DuplicateToken failed
throw
new Exception(sResult);
}
else
{
//
create new identity using new primary token
WindowsIdentity
newId = new WindowsIdentity(pDuplicateTokenHandle);
WindowsImpersonationContext
impersonatedUser = newId.Impersonate();
return
impersonatedUser;
////
check the identity after impersonation
//sResult
+= "After impersonation: " + WindowsIdentity.GetCurrent().Name +
"\r\n";
//throw
new Exception(sResult);
}
}
catch
(Exception ex)
{
throw
ex;
}
finally
{
//
close handle(s)
if
(pExistingTokenHandle != IntPtr.Zero)
CloseHandle(pExistingTokenHandle);
if
(pDuplicateTokenHandle != IntPtr.Zero)
CloseHandle(pDuplicateTokenHandle);
}
}
}
}
Fantástica clase. Yo estoy haciendo lo mismo que tu hiciste. Tengo el mismo problema, no puedo abrir el pdf que se encuentra en el servidor porque el usuario que sí accede a la carpeta donde está el archivo PDF no tiene permisos para usar el Acrobat de la máquina donde se ha lanzado la aplicación. ¿Podrías ayudarme? No se como poner el código para que se pueda abrir.
ResponderEliminarBuenas tardes.
ResponderEliminarAntes de nada agradecerte mucho el código que nos has regalado. Es de mucha utilidad.
No estoy muy puesto en esto de la impersonación pero me parece muy util para el tema de seguridad de aplicaciones.
En la primera parte comentas que para restaurar los valores originales tras la acción que se realice con la impersonación debemos utilizar la función Undo, pero, ¿esa función pertenece a la clase Impersonate?, porque no la encuentro o pertenece a otra clase.
Muchas gracias.