Impersonating another user with the Windows Form Report Viewer control and SQL Server Reporting Services 2005
The Winform Report Viewer control is pretty nifty, but occasionally you'll want to access a remote report server using a different identity than the user logged in at the console. It can be done, but takes a tiny bit of work.
You're going to be calling some native Win32 functions to get this to work, namely Logonuser, DuplicateToken, and CloseHandle
I'm also being a little bit lazy here, and am just copying in the complete source from the Winform I created the example in. I'm sure you can clean this up quite a bit more.
WARNING: Notice how I'm pretty much hard coding user credentials in the sample below? Isn't that a dumb thing to do? In your code you won't do the same thing, right? (Answers at the bottom of this post)
OK, so here's what we're doing:
Creating a Token, and passing it to the LogonUser function while we logon with a different set of credentials
Making a copy of the token
Creating a WindowsIdentity object off the duplicate token
Assigning this WindowsIdentity object (newId) to the Report Viewer control, specifically reportViewer1.ServerReport.ReportServerCredentials.ImpersonationUser
BTW, this sample also assumes that you have already set properties of the Report Viewer control (ProcessingMode, ReportPath, ReportServerUrl etc.) using the IDE....
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using MyCode.localhost;
using System.Xml;
using System.Web.Services.Protocols;
using System.IO;
using System.Diagnostics;
using System.Security.Principal;
using System.Runtime.InteropServices;
namespace SampleCode
{
public partial class Form2 : Form
{
[DllImport("advapi32.dll", SetLastError=true)] public extern static bool LogonUser(String lpszUsername, String lpszDomain,
String lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);
[DllImport("kernel32.dll", CharSet=CharSet.Auto)]
public extern static bool CloseHandle(IntPtr handle);
[DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)]
public extern static bool DuplicateToken(IntPtr ExistingTokenHandle,
int SECURITY_IMPERSONATION_LEVEL, ref IntPtr DuplicateTokenHandle);
public Form2()
{
InitializeComponent();
}
private void Form2_Load(object sender, EventArgs e)
{
// Impersonation code STOLEN from Q319615...shame on me, I feel dirty.
IntPtr tokenHandle = new IntPtr(0);
IntPtr dupeTokenHandle = new IntPtr(0);
string[] args = new string[3] { "Domain", "user", "password" };
// args[0] - DomainName
// args[1] - UserName
// args[2] - Password
const int LOGON32_PROVIDER_DEFAULT = 0;
//This parameter causes LogonUser to create a primary token.
const int LOGON32_LOGON_INTERACTIVE = 2;
const int SecurityImpersonation = 2;
tokenHandle = IntPtr.Zero;
dupeTokenHandle = IntPtr.Zero;
try
{
// Call LogonUser to obtain an handle to an access token.
bool returnValue = LogonUser(args[1], args[0], args[2],
LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT,
ref tokenHandle);
if (false == returnValue)
{
Console.WriteLine("LogonUser failed with error code : {0}",
Marshal.GetLastWin32Error());
return;
}
// Check the identity.
Console.WriteLine("Before impersonation: "
+ WindowsIdentity.GetCurrent().Name);
bool retVal = DuplicateToken(tokenHandle, SecurityImpersonation, ref dupeTokenHandle);
if (false == retVal)
{
CloseHandle(tokenHandle);
Console.WriteLine("Exception in token duplication.");
return;
}
// The token that is passed to the following constructor must
// be a primary token to impersonate.
WindowsIdentity newId = new WindowsIdentity(dupeTokenHandle);
WindowsImpersonationContext impersonatedUser = newId.Impersonate();
// Check the identity.
Console.WriteLine("After impersonation: "
+ WindowsIdentity.GetCurrent().Name);
// Assign the new identity to the R.V. control and Refresh the control
reportViewer1.ServerReport.ReportServerCredentials.ImpersonationUser = newId;
reportViewer1.RefreshReport();
// Stop impersonating.
impersonatedUser.Undo();
// Check the identity.
Console.WriteLine("After Undo: " + WindowsIdentity.GetCurrent().Name);
// Free the tokens.
if (tokenHandle != IntPtr.Zero)
CloseHandle(tokenHandle);
if (dupeTokenHandle != IntPtr.Zero)
CloseHandle(dupeTokenHandle);
}
catch (Exception ex)
{
Console.WriteLine("Exception occurred. " + ex.Message);
}
}
}
}
(Answers: Yes, I did / Yes, it is / NO, I won't.)