This impersonate class has been designed to enable the running of code segment a user under different security context to
that which is the norm. The typical example is allowing the ASP.NET account to have access to network drives
while the running in the context of the ASP.NET account. This class by design implements the IDisposable
interface allowing the context to automatically revert with a using statement.
Namespace:
CA.Common.SecurityAssembly: CA.Common (in CA.Common.dll) Version: 1.0.0.0 (1.0.0.0)
Remarks
Using the dispose method makes it possible to use the Impersonate class with a using class in c#
CopyQuick Example of using Impersonate class using the C# using statement
CopyFull source code for the Impersonate class
using (new Impersonate(User, Domain, Password)) { //.. do work with new security context }
//Source code from the Code Associate C# code library, Full documentation and latest updates can be found //@ http://www.codeassociate.com/caapi/ using System; using System.Runtime.InteropServices; using System.Security.Principal; namespace CA.Common.Security { public class ImpersonateException : Exception { public ImpersonateException(string Message) : base(Message) { } } public class Impersonate : IDisposable { private const int LOGON32_LOGON_INTERACTIVE = 2; private const int LOGON32_PROVIDER_DEFAULT = 0; - #region windows API calls [DllImport("advapi32.dll")] private static extern int LogonUserA(String lpszUserName, String lpszDomain, String lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken); [DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)] private static extern int DuplicateToken(IntPtr hToken, int impersonationLevel, ref IntPtr hNewToken); [DllImport("advapi32.dll", CharSet=CharSet.Auto, SetLastError=true)] private static extern bool RevertToSelf(); [DllImport("kernel32.dll", CharSet=CharSet.Auto)] private static extern bool CloseHandle(IntPtr handle); #endregion readonly WindowsImpersonationContext impersonationContext; public Impersonate(string userName, string domain, string password) { IntPtr token = IntPtr.Zero; IntPtr tokenDuplicate = IntPtr.Zero; // firstly force any current Impersonation to revert to the default security context if(RevertToSelf()) { // Call the LogonUserA to authenticate the user and get the users token if(LogonUserA(userName, domain, password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref token) != 0) { //The DuplicateToken function creates an impersonation token which can be used when creating a new windows identity if(DuplicateToken(token, 2, ref tokenDuplicate) != 0) { WindowsIdentity tempWindowsIdentity; tempWindowsIdentity = new WindowsIdentity(tokenDuplicate); impersonationContext = tempWindowsIdentity.Impersonate(); // we have the impersonationContext so we can close the handles to the tokens if (impersonationContext != null) { CloseHandle(token); CloseHandle(tokenDuplicate); } } else { if (token != IntPtr.Zero) CloseHandle(token); if(tokenDuplicate!=IntPtr.Zero) CloseHandle(tokenDuplicate); throw new ImpersonateException(string.Format("Impersonation Failed - DuplicateToken ({0})", userName)); } } else { if(token!= IntPtr.Zero) CloseHandle(token); throw new ImpersonateException(string.Format("Impersonation Failed - LogonUserA ({0})", userName)); } } else { throw new ImpersonateException("Impersonation Failed - RevertToSelf"); } } public void Dispose() { impersonationContext.Undo(); } } }
Examples
//Source code from the Code Associate C# code library, Full documentation and latest updates can be found //@ http://www.codeassociate.com/caapi/ using System.Configuration; using System.Diagnostics; using System.Security.Principal; using CA.Common.Security; using NUnit.Framework; namespace CA.Common.UnitTest.Security { [TestFixture] public class Impersonate_UnitTest { [Test] public void TestValidImpersonate() { // get the user name password and domain from the config file string User = ConfigurationManager.AppSettings["ImpersonateTestUser"].ToString(); string Password = ConfigurationManager.AppSettings["ImpersonateTestPassword"].ToString(); string Domain = ConfigurationManager.AppSettings["ImpersonateTestDomain"].ToString(); // Now get the user name currently running string BeforeImpersonateorIdentityName = WindowsIdentity.GetCurrent().Name; Trace.WriteLine(BeforeImpersonateorIdentityName); // Now begin the Impersonate under a new account using (new Impersonate(User, Domain, Password)) { // this is is expected name after the Impersonate string ExpectedIdentityName = Domain + "\\" + User; // Now get the current WindowsIdentity name inside the Impersonate string ActualIdentityNameInImpersonate = WindowsIdentity.GetCurrent().Name; Trace.WriteLine(ActualIdentityNameInImpersonate); // test that the expected is the same as the ActualIdentityNameInImpersonate do ToLower as names and domains are case insensitive Assert.AreEqual(ExpectedIdentityName.ToLower(), ActualIdentityNameInImpersonate.ToLower()); } // get the Name after the Impersonate section is done.. this should revert the identity string AfterImpersonateorIdentityName = WindowsIdentity.GetCurrent().Name; Trace.WriteLine(AfterImpersonateorIdentityName); // do the test the the BeforeImpersonateorIdentityName is the same as the AfterImpersonateorIdentityName Assert.AreEqual(BeforeImpersonateorIdentityName.ToLower(), AfterImpersonateorIdentityName.ToLower()); } [Test] [ExpectedException(typeof(ImpersonateException))] public void TestInvalidImpersonate() { using (Impersonate Impersonateor = new Impersonate("baduser", "workgroup", "badPassword")) { // This should should never get executed as Impersonate should fail // and raise the expected ImpersonateException unless you have the user password domain combo as hard coded } } } }