Как запустить тесты Internet Explorer Selenium от имени определенного пользователя домена?

У меня есть веб-сайт ASP.NET MVC, который использует проверку подлинности Windows для управления доступом. Я хотел бы иметь тест селена specflow, который проверяет правильность конфигурации, пытаясь посетить сайт в качестве неавторизованного пользователя.

Поскольку мы используем учетные записи домена для управления доступом, экран входа в систему с именем пользователя и паролем отсутствует. Учетные данные текущего пользователя автоматически передаются на сайт браузером.

Поэтому для моего теста Selenium мне нужно иметь возможность запускать Internet Explorer от имени конкретного пользователя.

Я нашел ряд статей о олицетворении Windows, и я могу переключиться на своего тестового пользователя во время выполнения теста (используя код из http://support.microsoft.com/kb/306158). Однако, если я затем создам InternetExplorerDriver, он запустит Internet Explorer с моими учетными данными, а не с учетными данными тестового пользователя (хотя этот вопрос и ответ предполагают, что он должен работать https://sqa.stackexchange.com/questions/2277/using-selenium-webdriver-with-windows-authentication).

Я также могу явно запустить процесс Internet Explorer в качестве своего тестового пользователя, но я не вижу способа связать InternetExplorerDriver с уже запущенным процессом Internet Explorer, так что это может оказаться тупиком.

Мой код, в основном взятый со страницы MSDN выше, приведен ниже. В отладчике я вижу, что WindowsIdentity.GetCurrent().Name имеет значение «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();
    }
}

}


comment
может быть, что-то попробовать: поместите свой экземпляр driver перед impersonateValidUser(..)   -  person ddavison    schedule 20.01.2015
comment
Спасибо за идею, но боюсь, что это ничего не исправит   -  person Dan    schedule 20.01.2015
comment
Может быть, попробуйте запустить свои тесты из командной строки Windows через runas /user:USER@DOMIAN path\to\mstest.exe ...   -  person Greg Burghardt    schedule 28.01.2015
comment
Это, вероятно, сработает, но вам нужно будет запустить один запуск для каждого пользователя. На данный момент я перестал заниматься этим, так как мне нужны только два теста, и я могу запустить их вручную за пару минут.   -  person Dan    schedule 03.02.2015
comment
Кто-то задал аналогичный вопрос в контексте Java: title="работает, т.е. как другой пользователь с веб-драйвером selenium в java"> stackoverflow.com/questions/18017883/ - но остается "без ответа".   -  person indra    schedule 18.02.2015


Ответы (8)


У нас есть много корпоративных клиентов, которые используют проверку подлинности Windows для приложений, работающих в интрасети, и мы начинаем запускать множество тестов Selenium для подтверждения, регрессии и т. д.

Мы взяли полезный код из ответа Стивена и преобразовали его в повторно используемый класс, аналогичный другим Impersonate сообщениям, которые просто не работали для нас, потому что мы хотели, чтобы тесты работали как локально в процессе разработки, так и развертывались как часть визуального Процесс выпуска 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's 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;
            }
        }
    }
}

Возможно, это поможет кому-то еще с той же проблемой.

person Rick Glos    schedule 01.06.2016
comment
Спасибо за этот класс; это действительно помогло мне понять, чем я хочу заниматься. Однако у меня возникли проблемы с информацией для входа в систему, и я написал об этом здесь: stackoverflow.com/questions/47687030/ Знаете ли вы, как я мог обойти это/что я сделал неправильно? Я получаю ту же ошибку при использовании вашего немодифицированного кода. - person Rescis; 08.12.2017
comment
Логика этого кода очень понятна: сначала запустить процесс драйвера хрома с помощью имперсонатора, затем подключиться к драйверу через Json Wire Protocol и RemoteWebDriver. Очень умно, молодец! - person Richard; 03.07.2020

Это на самом деле возможно. Я столкнулся с той же проблемой, что и у вас. В основном, вот шаги, которые вам нужно сделать.

  1. Запустите драйвер браузера вручную с учетными данными другого пользователя в фоновом режиме.

    Process driverProcess;
    string driverPath; // The path to Selenium's 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();
    
  2. Используйте Remote Web Driver для подключения экземпляра драйвера браузера, запущенного вручную.

    var remoteDriver = new RemoteWebDriver(Uri("http://localhost:5555"), DesiredCapabilities.InternetExplorer());
    
  3. Не забудьте закрыть/выйти/завершить процесс драйвера и экземпляр браузера, когда закончите.

    // 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;
    }
    
person Steven    schedule 12.11.2015
comment
В шаге 1 отсутствует способ запуска процесса вручную, в частности, объект info. - person Rick Glos; 31.05.2016
comment
@RickGlos Я обновил шаг 1, включив в него способ получения объекта info. - person Steven; 31.05.2016
comment
спасибо, с этим дополнительным лакомым кусочком ясности я смог заставить это работать, хотя мы делаем это с Chrome, кажется, что это так же просто, как заменить driverPath на использование chromedriver вместо IEDriverServer. Нам также нужно повторно использовать это снова и снова, поэтому я вскоре опубликую ответ, который мы используем сейчас во многих методах, основанных на вашем. Еще раз спасибо! - person Rick Glos; 01.06.2016

Этот похожий вопрос ссылается на этот Статья службы поддержки Microsoft. По существу вам нужно

System.Security.Principal.WindowsImpersonationContext impersonationContext;
impersonationContext = 
((System.Security.Principal.WindowsIdentity)User.Identity).Impersonate();
IWebDriver webDriver = new InternetExplorerDriver();
// do your stuff here.
impersonationContext.Undo();

В статье поддержки есть дополнительный код о выдаче себя за конкретного пользователя.

person Scott Rickman    schedule 10.02.2015
comment
Думаю, Дэн уже использует этот код. Это та же статья в базе знаний, на которую он ссылался в своем вопросе. - person Rup; 10.02.2015
comment
олицетворение работает, но пользователь Internet Explorer не меняется для меня. - person ColacX; 12.02.2015

У вас есть пара старых компьютеров? Или емкость для некоторых виртуальных машин?

Если это так, создайте настройку Selenium Grid и настройте один для автоматического входа в качестве нужного пользователя домена, а другой — в качестве пользователя без домена.
http://code.google.com/p/selenium/wiki/Grid2

person Bigwave    schedule 19.02.2015

У меня была такая же проблема, когда я делал проект автоматизации для веб-приложения, которое требовало аутентификации окна. Тем не менее, я добился этого с помощью Firefox, и ниже приведены шаги для его достижения.

НАСТРОЙКА FIREFOX

  1. ОТКРОЙТЕ ДИАЛОГ ЗАПУСКА ВАШЕЙ СИСТЕМЫ И ВВЕДИТЕ 'firefox.exe -p' (ЗАКРОЙТЕ БРАУЗЕР FIREFOX ПЕРЕД ЗАПУСКОМ ЭТОЙ КОМАНДЫ) http://www.wikihow.com/Create-a-Firefox-Profile
  2. НАЖМИТЕ СОЗДАТЬ ПРОФИЛЬ И ДАЙТЕ ИМЯ, КАК ТРЕБУЕТСЯ
  3. ВЫБРАТЬ СОЗДАННЫЙ ПРОФИЛЬ И ЗАПУСТИТЬ БРАУЗЕР И ОТКРЫТЬ МЕНЕДЖЕР ДОПОЛНЕНИЙ (ИНСТРУМЕНТЫ - ДОПОЛНЕНИЯ)
  4. НАЙДИТЕ «AutoAuth» И УСТАНОВИТЕ ЕГО. ОН ПОПРОСИТ ПЕРЕЗАПУСТИТЬ, СДЕЛАЙТЕ ЭТО
  5. ПОСЛЕ ПЕРЕЗАПУСКА FIREFOX, ОТКРЫТЫЙ URL ОН ПОПРОБУЕТ У ВАС АУТЕНТИФИКАЦИЮ
  6. ВВЕДИТЕ ИМЯ ПОЛЬЗОВАТЕЛЯ И ПАРОЛЬ - ОТПРАВЬТЕ ЕГО, FIREFOX ПОПРОСИТ ВАС ЗАПОМНИТЬ ПАРОЛЬ
  7. НАЖМИТЕ НА ЗАПОМНИТЬ И ЭТО СОХРАНИТ ПАРОЛЬ В ПРОФИЛЬ FIREFOX
  8. СКОПИРУЙТЕ СОЗДАННЫЙ ПРОФИЛЬ Firefox И СОХРАНИТЕ ЕГО В НУЖНУЮ ПАПКУ
  9. В ВАШЕМ СЦЕНАРИИ SELENIUM ВЫЗОВИТЕ ВЫШЕ СОЗДАННЫЙ ПРОФИЛЬ С ДРАЙВЕРОМ FIREFOX И ПЕРЕДАЙТЕ ЖЕ URL-адрес, ОН НЕ БУДЕТ ЗАПРОСИТЬ ДИАЛОГ АУТЕНТИФИКАЦИИ

Это работает очень успешно в моем проекте.

person Karim Narsindani    schedule 05.07.2015

Мы используем подход https://stackoverflow.com/a/31540010/3489693 для IE и Chrome более 2 лет. Он отлично работает

person mdementev    schedule 21.07.2015

Таким образом, кажется, что проблема, которую пытается обойти вопрос, связана с автоматическим входом в систему NTLM. См. Автоматический вход в Google Chrome и NTLM с использованием проверки подлинности Windows.

Приведенные выше решения не сработали для меня, поскольку автоматический вход в систему успешно аутентифицировал любого пользователя в моей системе, поэтому не имело значения, какого пользователя я использовал для олицетворения.

Однако я заметил, что вы можете перехитрить автоматический вход, заменив localhost любым другим доменным именем, например локальным IP-адресом. Никакого олицетворения не требуется :)

person bgh    schedule 23.06.2016

Это может/может не сработать.

  • Попробуйте запустить свой сайт в "ХРОМЕ".
  • Нажмите F-12, перейдите на вкладку «Приложения» -> «Файлы cookie» -> нажмите на ссылку на свой сайт. слева найдите что-то, что представляет идентификатор вашего сеанса, может быть JSESSIONID или аналогичный, который представляет сеанс пользователя, скопируйте это.
  • Теперь откройте Internet Explorer,
  • нажмите F-12 и вручную создайте этот JSESSIONID (или аналогичный ключ), выполнив эту команду в окне консоли

document.cookie = "JSESSIONID=ваш-идентификатор-сессии-из-хрома"

  • нажмите кнопку воспроизведения, чтобы выполнить скрипт
  • Обновите браузер
person ATHER    schedule 20.02.2019