特定のユーザーに代わってコードを実行する

問題の声明





次の状況を想像してください:userAだけがアクセスできるディレクトリAがあり、Bディレクトリがあるので、userBだけがアクセスできます。ユーザーuserC、userDがプログラムを開始できる場合、ディレクトリAからディレクトリBにファイルをコピーする必要があります。 userF、...など



ドラフトソリューション





ソリューションの1つとして、次のスキームを使用できます。

プログラムは任意のユーザーに代わって実行され、userAとしてログインし、ファイルをメモリに読み込み、最初のユーザーに戻り、userBとしてログインし、ファイルを宛先ディレクトリに書き込み、元のユーザーに戻ります。 ファイルサイズが大きい場合、ブロッキングを考えることができますが、これは事実ではありません。





解決策





3つの単純なクラスが必要です(実際、最初のクラスで十分です)。



[SecurityPermission(SecurityAction.InheritanceDemand, UnmanagedCode = true)] [SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)] internal class MySafeTokenHandle : SafeHandleZeroOrMinusOneIsInvalid { private MySafeTokenHandle() : base(true) { } [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] override protected bool ReleaseHandle() { return NativeMethods.CloseHandle(handle); } } [SuppressUnmanagedCodeSecurity()] internal static class NativeMethods { #region P/Invoke internal const int LOGON32_LOGON_INTERACTIVE = unchecked((int)2); internal const int LOGON32_PROVIDER_DEFAULT = unchecked((int)0); [DllImport("advapi32.dll")] internal static extern int LogonUserA(String lpszUserName, String lpszDomain, String lpszPassword, int dwLogonType, int dwLogonProvider, out MySafeTokenHandle phToken); [DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)] internal static extern int DuplicateToken(MySafeTokenHandle hToken, int impersonationLevel, out MySafeTokenHandle hNewToken); [DllImport("kernel32.dll", CharSet = CharSet.Auto)] [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] internal static extern bool CloseHandle(IntPtr handle); #endregion } /// <summary> ///          /// </summary> [SecurityPermissionAttribute(SecurityAction.InheritanceDemand, UnmanagedCode = true)] public class Impersonation : IDisposable { WindowsImpersonationContext impersonationContext; /// <summary> ///      /// </summary> /// <param name="userName"> </param> /// <param name="domain"></param> /// <param name="password"></param> /// <returns>false -        </returns> public void ImpersonateUser(String userName, String domain, String password) { WindowsIdentity tempWindowsIdentity; MySafeTokenHandle token; MySafeTokenHandle tokenDuplicate; if (NativeMethods.LogonUserA(userName, domain, password, NativeMethods.LOGON32_LOGON_INTERACTIVE, NativeMethods.LOGON32_PROVIDER_DEFAULT, out token) != 0) { using (token) { if (NativeMethods.DuplicateToken(token, 2, out tokenDuplicate) != 0) { using (tokenDuplicate) { if (!tokenDuplicate.IsInvalid) { tempWindowsIdentity = new WindowsIdentity(tokenDuplicate.DangerousGetHandle()); impersonationContext = tempWindowsIdentity.Impersonate(); return; } } } } } else throw new Exception("LogonUser failed: " + Marshal.GetLastWin32Error().ToString()); } /// <summary> ///       /// </summary> public void Dispose() { impersonationContext.Undo(); GC.SuppressFinalize(this); } }
      
      







2番目のクラスはコードの実行を担当します。

 /// <summary> ///         /// </summary> public class ImpersonationExecutor { public string USR { get; set; } public string DOMAIN { get; set; } public string PWD { get; set; } /// <summary> ///  /// </summary> /// <param name="userName"> </param> /// <param name="domainName"></param> /// <param name="password"></param> public ImpersonationExecutor(string userName, string domainName, string password) { USR = userName; DOMAIN = domainName; PWD = password; } /// <summary> ///   /// </summary> /// <typeparam name="T">  </typeparam> /// <param name="action"></param> /// <param name="arg"></param> public void ExecuteCode<T>(Action<T> action, T arg) { using (Impersonation user = new Impersonation()) { user.ImpersonateUser(USR, DOMAIN, PWD); action(arg); } } }
      
      







そして最後に、非常に小さな工場:

 public class ImpersonationFactory { public static ImpersonationExecutor Create(string UserName, string Domain, string Password) { return new ImpersonationExecutor(UserName, Domain, Password); } }
      
      







使用例



例として、ステートメントで提起された問題を解決します。

 string from = "from"; string to = "to"; byte[] buffer = null; ImpersonationFactory.Create("userA", "domainA", "passwordA").ExecuteCode<string>(delegate(string path) { buffer = File.ReadAllBytes(path); }, from); ImpersonationFactory.Create("userB", "domainB", "passwordB").ExecuteCode<string>(delegate(string path) { File.WriteAllBytes(path, buffer); }, to);
      
      







まとめ



問題は解決しました。 または、異なるユーザーで実行されている異なるアプリケーション(サービス)を使用することもできましたが、柔軟性は失われます。 他の方法を見てうれしいです。

ハブでの検索では、この特定のソリューションは見つかりませんでした。



UPD :bobermaniac ユーザーによると、偽装クラスは修正されています



All Articles