我的Winforms应用根据当前流程中找到的群组成员资格设置权限。
我刚刚在MSTEST进行了单元测试。
我想以其他用户身份运行它,以便我可以验证预期的行为。
以下是我的拍摄方式:
[TestMethod]
public void SecuritySummaryTest1()
{
Impersonate(@"SomeDomain\AdminUser", password);
var target = new DirectAgentsSecurityManager();
string actual = target.SecuritySummary;
Assert.AreEqual(
@"Default=[no]AccountManagement=[no]MediaBuying=[no]AdSales=[no]Accounting=[no]Admin=[YES]", actual);
}
[TestMethod]
public void SecuritySummaryTest2()
{
Impersonate(@"SomeDomain\AccountantUser", password);
var target = new DirectAgentsSecurityManager();
string actual = target.SecuritySummary;
Assert.AreEqual(
@"Default=[no]AccountManagement=[YES]MediaBuying=[no]AdSales=[no]Accounting=[YES]Admin=[NO]", actual);
}
答案 0 :(得分:9)
public class UserCredentials
{
private readonly string _domain;
private readonly string _password;
private readonly string _username;
public UserCredentials(string domain, string username, string password)
{
_domain = domain;
_username = username;
_password = password;
}
public string Domain { get { return _domain; } }
public string Username { get { return _username; } }
public string Password { get { return _password; } }
}
public class UserImpersonation : IDisposable
{
private readonly IntPtr _dupeTokenHandle = new IntPtr(0);
private readonly IntPtr _tokenHandle = new IntPtr(0);
private WindowsImpersonationContext _impersonatedUser;
public UserImpersonation(UserCredentials credentials)
{
const int logon32ProviderDefault = 0;
const int logon32LogonInteractive = 2;
const int securityImpersonation = 2;
_tokenHandle = IntPtr.Zero;
_dupeTokenHandle = IntPtr.Zero;
if (!Advapi32.LogonUser(credentials.Username, credentials.Domain, credentials.Password,
logon32LogonInteractive, logon32ProviderDefault, out _tokenHandle))
{
var win32ErrorNumber = Marshal.GetLastWin32Error();
// REVIEW: maybe ImpersonationException should inherit from win32exception
throw new ImpersonationException(win32ErrorNumber, new Win32Exception(win32ErrorNumber).Message,
credentials.Username, credentials.Domain);
}
if (!Advapi32.DuplicateToken(_tokenHandle, securityImpersonation, out _dupeTokenHandle))
{
var win32ErrorNumber = Marshal.GetLastWin32Error();
Kernel32.CloseHandle(_tokenHandle);
throw new ImpersonationException(win32ErrorNumber, "Unable to duplicate token!", credentials.Username,
credentials.Domain);
}
var newId = new WindowsIdentity(_dupeTokenHandle);
_impersonatedUser = newId.Impersonate();
}
public void Dispose()
{
if (_impersonatedUser != null)
{
_impersonatedUser.Undo();
_impersonatedUser = null;
if (_tokenHandle != IntPtr.Zero)
Kernel32.CloseHandle(_tokenHandle);
if (_dupeTokenHandle != IntPtr.Zero)
Kernel32.CloseHandle(_dupeTokenHandle);
}
}
}
internal static class Advapi32
{
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool DuplicateToken(IntPtr ExistingTokenHandle, int SECURITY_IMPERSONATION_LEVEL,
out IntPtr DuplicateTokenHandle);
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool LogonUser(string lpszUsername, string lpszDomain, string lpszPassword,
int dwLogonType, int dwLogonProvider, out IntPtr phToken);
}
internal static class Kernel32
{
[DllImport("kernel32.dll", SetLastError = true)]
[return : MarshalAs(UnmanagedType.Bool)]
public static extern bool CloseHandle(IntPtr hObject);
}
我没有包含ImpersonationException的实现,但这并不重要。它没有做任何特别的事情。
答案 1 :(得分:4)
如果这对您的用例足够,您也可以直接设置当前主体:
System.Threading.Thread.CurrentPrincipal
= new WindowsPrincipal(new WindowsIdentity("testuser@contoso.com"));
根据此connect page,在每种测试方法之后恢复主体。 请注意,如果与检查主体的Web服务客户端一起使用,此方法将不起作用(对于此用例,Jim Bolla的解决方案工作正常)。
答案 2 :(得分:2)
您应该使用Mock对象来模拟不同状态的依赖对象。 有关模拟框架的示例,请参阅moq:
您需要抽象出为接口提供当前用户的位。并将该接口的模拟传递给被测试的类。
答案 3 :(得分:1)
要添加到Markus解决方案的另一件事,您可能还需要将HttpContext.Current.User设置为您正在创建/模拟对RoleManager的某些调用的Thread.CurrentPrincipal(例如:Roles.GetRolesForUser(Identity.Name))如果你使用方法的无参数版本,则不需要这个,但我有一个需要传递用户名的授权基础设施。
使用模拟的Thread.CurrentPrincipal调用该方法签名将失败, “仅当用户名参数与当前Windows标识中的用户名匹配时才支持方法”。 如消息所示,WindowsTokenRoleProvider代码中存在针对“HttpContext.Current.Identity.Name”的内部检查。如果它们不匹配,则该方法失败。
以下是演示Action授权的ApiController的示例代码。我使用模拟进行单元测试和集成测试,因此我可以在不同的AD角色下进行QA,以确保在部署之前安全性正常。
using System.Web
List<string> WhoIsAuthorized = new List<string>() {"ADGroup", "AdUser", "etc"};
public class MyController : ApiController {
public MyController() {
#if TEST
var myPrincipal = new WindowsPrincipal(new WindowsIdentity("testuser@contoso.com"));
System.Threading.Thread.CurrentPrincipal = myPrincipal;
HttpContext.Current.User = myPrincipal;
#endif
}
public HttpResponseMessage MyAction() {
var userRoles = Roles.GetRolesForUser(User.Identity.Name);
bool isAuthorized = userRoles.Any(role => WhoIsAuthorized.Contains(role));
}
}
希望这有助于其他人:)
答案 4 :(得分:1)
运行Install-Package SimpleImpersonation
以安装nuget软件包。
然后
var credentials = new UserCredentials(domain, username, password);
Impersonation.RunAsUser(credentials, LogonType.NewCredentials, () =>
{
// Body of the unit test case.
});
这是最简单,最优雅的解决方案。