Как запустить тесты Internet Explorer Selenium в качестве конкретного пользователя домена?
У меня есть веб-сайт ASP.NET MVC, который использует проверку подлинности Windows для контроля доступа. Я хотел бы иметь тест селеневого потока, который проверяет правильность конфигурации, пытаясь посетить сайт как неавторизованный пользователь.
Поскольку мы используем учетные записи домена для контроля доступа, нет экрана входа имени пользователя и пароля. Учетные данные текущего пользователя автоматически передаются на сайт браузером.
Итак, для моего теста Selenium мне нужно иметь возможность запускать Internet Explorer в качестве конкретного пользователя.
Я нашел несколько статей о олицетворении окон, и я могу переключиться на своего тестового пользователя во время запуска теста (используя код из http://support.microsoft.com/kb/306158). Однако, если я создаю InternetExplorerDriver, он запускает Internet Explorer с моими учетными данными, а не тестовым пользователем (хотя этот вопрос и ответ подсказывают, что он должен работать https://sqa.stackexchange.com/info/2277/using-selenium-webdriver-with-windows-authentication).
Я также могу явно запустить процесс Internet Explorer в качестве моего тестового пользователя, но я не вижу способа привязки InternetExplorerDriver к уже запущенному процессу Internet Explorer, поэтому это может быть тупиком.
Мой код, в основном взятый с приведенной выше страницы MSDN, приведен ниже. В отладчике я вижу, что WindowsIdentity.GetCurrent(). Имя является "testUser" на всех этапах теста.
namespace MyProject.Specs
{
using NUnit.Framework;
using OpenQA.Selenium;
using OpenQA.Selenium.IE;
using System;
using System.Runtime.InteropServices;
using System.Security.Principal;
using TechTalk.SpecFlow;
[Binding]
public class AuthorisationSteps
{
public const int LOGON32_LOGON_INTERACTIVE = 2;
public const int LOGON32_PROVIDER_DEFAULT = 0;
private static WindowsImpersonationContext impersonationContext;
private static IWebDriver driver;
[BeforeScenario]
public static void impersonateUser()
{
if (!impersonateValidUser("testUser", "testDomain", "password"))
{
throw new Exception();
}
driver = new InternetExplorerDriver();
}
[AfterScenario]
public static void cleanupUser()
{
undoImpersonation();
driver.Quit();
}
[Given(@"I am an unauthorised user")]
public void GivenIAmAnUnauthorisedUser()
{
var temp = WindowsIdentity.GetCurrent().Name;
}
[When(@"I go to the home page")]
public void WhenIGoToTheHomePage()
{
var temp = WindowsIdentity.GetCurrent().Name;
driver.Navigate().GoToUrl(BaseUrl);
}
[Then(@"I should see an error page")]
public void ThenIShouldSeeAnErrorPage()
{
var temp = WindowsIdentity.GetCurrent().Name;
Assert.That(driver.Title.Contains("Error"));
}
[DllImport("advapi32.dll")]
public 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)]
public static extern int DuplicateToken(IntPtr hToken,
int impersonationLevel,
ref IntPtr hNewToken);
[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool RevertToSelf();
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public static extern bool CloseHandle(IntPtr handle);
private static bool impersonateValidUser(String userName, String domain, String password)
{
WindowsIdentity tempWindowsIdentity;
var token = IntPtr.Zero;
var tokenDuplicate = IntPtr.Zero;
if (RevertToSelf())
{
if (LogonUserA(userName, domain, password, LOGON32_LOGON_INTERACTIVE,
LOGON32_PROVIDER_DEFAULT, ref token) != 0)
{
if (DuplicateToken(token, 2, ref tokenDuplicate) != 0)
{
tempWindowsIdentity = new WindowsIdentity(tokenDuplicate);
impersonationContext = tempWindowsIdentity.Impersonate();
if (impersonationContext != null)
{
CloseHandle(token);
CloseHandle(tokenDuplicate);
return true;
}
}
}
}
if (token != IntPtr.Zero)
{
CloseHandle(token);
}
if (tokenDuplicate != IntPtr.Zero)
{
CloseHandle(tokenDuplicate);
}
return false;
}
private static void undoImpersonation()
{
impersonationContext.Undo();
}
}
}
Ответы
Ответ 1
Это действительно возможно. Я столкнулся с точной проблемой. В основном, вот шаги, которые вам нужно выполнить.
-
Запустите драйвер браузера вручную с другими учетными данными пользователя в фоновом режиме
Process driverProcess;
string driverPath; // The path to Selenium IE driver.
ProcessStartInfo info = new ProcessStartInfo(driverPath)
{
UserName = "UserName", // The user name.
Password = new SecureString(), // The password for the user.
UseShellExecute = false,
LoadUserProfile = true,
Arguments = "about:blank"
};
// Start the driver in background thread
Thread startThread = new Thread(
() => {
try
{
driverProcess = Process.Start(info);
driverProcess.WaitForExit();
}
catch
{
// Close the process.
}
})
{
IsBackground = true
};
startThread.Start();
-
Используйте Remote Web Driver для подключения экземпляра драйвера браузера, запущенного вручную.
var remoteDriver = new RemoteWebDriver(Uri("http://localhost:5555"), DesiredCapabilities.InternetExplorer());
-
Не забудьте закрыть/закрыть/завершить процесс драйвера и экземпляр браузера, когда вы закончите.
// Close the process when done.
if (driverProcess != null)
{
// Free managed resources
if (!driverProcess.HasExited)
{
driverProcess.CloseMainWindow();
driverProcess.WaitForExit(5000);
// Kill the process if the process still alive after the wait
if (!driverProcess.HasExited)
{
driverProcess.Kill();
}
driverProcess.Close();
}
driverProcess.Dispose();
driverProcess = null;
}
Ответ 2
У нас есть много корпоративных клиентов, которые используют проверку подлинности Windows для приложений, обращенных к интрасети, и мы запускаем много тестов Selenium для подтверждения, регрессии и т.д.
Мы взяли полезный код из ответа Стивена и реорганизовали его в повторно используемый класс, похожий на другие сообщения Impersonate
, которые просто не работали для нас, потому что мы хотели, чтобы тесты работали как локально в разработке, так и развернуты как часть процесса выпуска Visual Studio Team System.
Метод uri не работал локально, и ни один из них не выдавал себя за использование методов Win32.
Это работало так, вот оно.
Пример теста с использованием кода Стивена, реорганизованного в помощник
[TestMethod]
public void ThisApp_WhenAccessedByUnathorizedUser_ShouldDisallowAccess()
{
string userName = "ThisAppNoAccess";
string password = "123456";
string domainName = Environment.MachineName;
using (new Perkins.Impersonator(userName, domainName, password))
{
// - Use Remote Web Driver to hook up the browser driver instance launched manually.
using (var driver = new RemoteWebDriver(new Uri("http://localhost:9515"), DesiredCapabilities.Chrome()))
{
var desiredUri = Helper.Combine(Helper.BaseURL, "/ThisApp/#/appGrid");
TestContext.WriteLine("desiredUri: {0}", desiredUri);
driver.Navigate().GoToUrl(desiredUri);
Helper.WaitForAngular(driver);
var noPermissionNotificationElement = driver.FindElementByXPath("//div[@ng-show='!vm.authorized']/div/div/div/p");
var showsNoPermissionNotification = noPermissionNotificationElement.Text.Contains("You do not have permissions to view ThisApp.");
Assert.AreEqual(true, showsNoPermissionNotification, "The text `You do not have permissions to view ThisApp.` is not being displayed!");
}
}
}
Вспомогательный класс
// Idea from http://stackoverflow.com/a/34406336/16008
// - Launch the browser driver manually with other user credentials in background
public class Perkins
{
public class Impersonator : IDisposable
{
Process _driverProcess = null;
string _driverPath = @"chromedriver.exe";
/// <summary>
/// Impersonates the specified user account by launching the selenium server under that account. Connect to it via RemoteWebDriver and localhost on port 9515.
/// </summary>
/// <remarks>
/// We may later want to enhance this by allowing for different ports, etc.
/// </remarks>
/// <param name="userName">Name of the user</param>
/// <param name="domainName">Name of the domain or computer if using a local account.</param>
/// <param name="password">The password</param>
public Impersonator(string userName, string domainName, string password)
{
ProcessStartInfo processStartInfo = new ProcessStartInfo(_driverPath);
processStartInfo.UserName = userName;
System.Security.SecureString securePassword = new System.Security.SecureString();
foreach (char c in password)
{
securePassword.AppendChar(c);
}
processStartInfo.Password = securePassword;
processStartInfo.Domain = domainName; // this is important, mcollins was getting a 'stub received bad data' without it, even though rglos was not
processStartInfo.UseShellExecute = false;
processStartInfo.LoadUserProfile = true; // this seemed to be key, without this, I get Internal Server Error 500
Thread startThread = new Thread(() =>
{
_driverProcess = Process.Start(processStartInfo);
_driverProcess.WaitForExit();
})
{ IsBackground = true };
startThread.Start();
}
public void Dispose()
{
// - Remember to close/exit/terminate the driver process and browser instance when you are done.
if (_driverProcess != null)
{
// Free managed resources
if (!_driverProcess.HasExited)
{
_driverProcess.CloseMainWindow();
_driverProcess.WaitForExit(5000);
// Kill the process if the process still alive after the wait
if (!_driverProcess.HasExited)
{
_driverProcess.Kill();
}
_driverProcess.Close();
}
_driverProcess.Dispose();
_driverProcess = null;
}
}
}
}
Возможно, это поможет кому-то еще с той же проблемой.
Ответ 3
Этот похожий вопрос ссылается на эту статью поддержки Microsoft. По сути вам нужно
System.Security.Principal.WindowsImpersonationContext impersonationContext;
impersonationContext =
((System.Security.Principal.WindowsIdentity)User.Identity).Impersonate();
IWebDriver webDriver = new InternetExplorerDriver();
// do your stuff here.
impersonationContext.Undo();
В статье поддержки содержится дополнительный код о выдаче себя за конкретного пользователя.
Ответ 4
Есть ли у вас несколько старых компьютеров? Или емкость для некоторых виртуальных машин?
Если это так, создайте настройку Selenium Grid и настройте ее для автоматического входа в систему как желаемого пользователя домена, а другой - для пользователя, не являющегося доменом.
http://code.google.com/p/selenium/wiki/Grid2
Ответ 5
У меня была такая же проблема, когда я делал проект автоматизации для веб-приложения, требующего проверки подлинности Windows. Тем не менее, я достиг этого с помощью firefox, следуя инструкциям по его достижению.
НАСТРОЙКА FIREFOX
- ОТКРЫТЬ ДИАЛОГ ИЗ ВАШЕЙ СИСТЕМЫ И ТИП 'firefox.exe -p' (ЗАКРЫВАЙТЕ ПРОСМОТР ПРОТИВ FIREFOX ПЕРЕД НАЧАЛОМ ЭТОЙ КОМАНДЫ) http://www.wikihow.com/Create-a-Firefox-Profile
- НАЖМИТЕ НА СОЗДАНИЕ ПРОФИЛЯ И ДАЙТЕ ИМЯ КАК REQUURIED
- ВЫБЕРИТЕ СОЗДАННЫЙ ПРОФИЛЬ И СТАРТ БРАУЗЕР И ОТКРЫТЫЙ МЕНЕДЖЕР ДОБАВЛЕНИЙ (ИНСТРУМЕНТЫ - ДОБАВКИ)
- ПОИСК "АвтоАвто" и УСТАНОВИТЕ ЭТО. ЭТО БУДЕТ ЗАПРЕЩАЕТСЯ ОТКРЫТЬ, ДЕЛАТЬ ЭТО.
- ОСТАВАЙТЕСЬ, ЧТО ПОКРЫВАЕТСЯ FIREFOX, ТО ОТКРЫТЫЙ URL ЭТО ПРОСИТ ВАС ДЛЯ АУТЕНТИФИКАЦИИ
- ВВОД ПОЛЬЗОВАТЕЛЯ И ПАРОЛЯ - ПРЕДОСТАВЛЯЙТЕ ЭТО, FIREFOX ПРОСИТ ВАС ПОМНИТЬ ПАРОЛЬ
- НАЖМИТЕ ПОМНИТЬ, И ЭТО СОХРАНИТЬ ПАРОЛЬ В ПРОФИЛЕ FIREFOX
- КОПИРОВАТЬ СОЗДАННЫЙ ПРОФИЛЬ FIREFOX И СОХРАНИТЬ, ЧТО ТРЕБУЕТСЯ ПАПКА
- В ВАШЕМ СЕЛЕНИИ SCRIPT ВЫБИРАЙТЕ ВЫШЕ СОЗДАННОГО ПРОФИЛЯ С ВОДИТЕЛЕЙ FIREFOX И ПРОХОДИТЕ ОДНОМ URL, ЭТО НЕ СПРОСИТЕ ДЛЯ ДИАЛОГА АУТЕНТИФИКАЦИИ
Это очень успешно работает в моем проекте.
Ответ 6
Мы используем fooobar.com/questions/355949/... для IE и Chrome более 2 лет. Он отлично работает
Ответ 7
Таким образом, проблема, с которой этот вопрос пытается обходить, связана с NTLM Auto Login. См. Автозапуск Google Chrome и NTLM с использованием проверки подлинности Windows
Решения, приведенные выше, не работали для меня, так как автоматический вход успешно прошел аутентификацию с любым пользователем в моей системе, поэтому неважно, какой пользователь я использовал для олицетворения.
Однако я заметил, что вы можете перехитрить автозапуск, заменив localhost
на любое другое доменное имя, например локальный IP-адрес. Не требуется олицетворения:)