Skip to content

Commit

Permalink
Merge pull request #42 from UWIT-IAM/GH-41
Browse files Browse the repository at this point in the history
[GH-41] Update eval and prod tests to accomodate for new "remember me" style cookie, and a few other ancillary/supporting changes.
  • Loading branch information
goulter authored Mar 4, 2024
2 parents 1c967aa + cccb234 commit 913e16e
Show file tree
Hide file tree
Showing 11 changed files with 92 additions and 51 deletions.
4 changes: 2 additions & 2 deletions tests/access_control/test_auto_2fa.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ def test_auto_2fa_c(self, netid3):
with self.browser.tab_context():
self.browser.get(self.sp_shib_url(sp, append='force'))
self.log_in_netid(self.browser, netid3, match_service_provider=sp, assert_success=False)
self.enter_duo_passcode(self.browser)
self.enter_duo_passcode(self.browser, is_this_your_device_screen=False)

def test_auto_2fa_d(self, netid3):
"""
Expand Down Expand Up @@ -78,7 +78,7 @@ def test_auto_2fa_e(self, netid3):
with self.utils.using_test_sp(sp):
self.browser.get(self.sp_shib_url(sp, append='mfaforce'))
self.log_in_netid(self.browser, netid3, match_service_provider=sp, assert_success=False)
self.enter_duo_passcode(self.browser, match_service_provider=sp)
self.enter_duo_passcode(self.browser, match_service_provider=sp, is_this_your_device_screen=False)

def test_auto_2fa_f(self, netid3):
"""
Expand Down
4 changes: 2 additions & 2 deletions tests/access_control/test_auto_2fa_cond_member.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def test_c(self):
with self.utils.using_test_sp(sp):
self.browser.get(self.sp_shib_url(sp, append='force'))
self.log_in_netid(self.browser, self.netid, assert_success=False)
self.enter_duo_passcode(self.browser, match_service_provider=sp)
self.enter_duo_passcode(self.browser, match_service_provider=sp, is_this_your_device_screen=False)

def test_d(self):
"""
Expand Down Expand Up @@ -81,7 +81,7 @@ def test_e(self):
with self.utils.using_test_sp(sp):
self.browser.get(self.sp_shib_url(sp, append='mfaforce'))
self.log_in_netid(self.browser, self.netid, assert_success=False)
self.enter_duo_passcode(self.browser, match_service_provider=sp)
self.enter_duo_passcode(self.browser, match_service_provider=sp, is_this_your_device_screen=False)

def test_f(self):
"""
Expand Down
11 changes: 8 additions & 3 deletions tests/access_control/test_auto_2fa_cond_non_member.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
import pytest
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait

from tests.access_control import AccessControlTestBase
from tests.models import ServiceProviderInstance
from selenium.webdriver.support import expected_conditions as EC


class TestAuto2faCondAccessNonMember(AccessControlTestBase):
@pytest.fixture(autouse=True)
def initialize(self, netid3):
def initialize(self, netid3, test_env):
self.netid = netid3
self.test_env = test_env

def test_a(self):
"""
Expand Down Expand Up @@ -49,7 +54,7 @@ def test_c(self):
with self.utils.using_test_sp(sp):
self.browser.get(self.sp_shib_url(sp, append='force'))
self.log_in_netid(self.browser, self.netid, assert_success=False)
self.enter_duo_passcode(self.browser, assert_success=False)
self.enter_duo_passcode(self.browser, assert_success=False, is_this_your_device_screen=False)
self.browser.switch_to.default_content()
self.browser.wait_for_tag('p', 'You are not authorized to access the application:')

Expand Down Expand Up @@ -83,7 +88,7 @@ def test_e(self):
with self.utils.using_test_sp(sp):
self.browser.get(self.sp_shib_url(sp, append='mfaforce'))
self.log_in_netid(self.browser, self.netid, assert_success=False)
self.enter_duo_passcode(self.browser, assert_success=False)
self.enter_duo_passcode(self.browser, assert_success=False, is_this_your_device_screen=False)
self.browser.switch_to.default_content()
self.browser.wait_for_tag('p', 'You are not authorized to access the application:')

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ def test_e(self, get_fresh_browser):
with self.utils.using_test_sp(sp):
self.browser.get(self.sp_shib_url(sp, append='mfaforce'))
self.log_in_netid(self.browser, self.netid, assert_success=False)
self.enter_duo_passcode(self.browser, match_service_provider=sp)
self.enter_duo_passcode(self.browser, match_service_provider=sp, is_this_your_device_screen=False)

def test_f(self):
"""
Expand Down
4 changes: 2 additions & 2 deletions tests/access_control/test_cond2fa_cond_member_both.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def test_c(self):
with self.utils.using_test_sp(sp):
self.browser.get(self.sp_shib_url(sp, append='force'))
self.log_in_netid(self.browser, self.netid, assert_success=False)
self.enter_duo_passcode(self.browser, match_service_provider=sp)
self.enter_duo_passcode(self.browser, match_service_provider=sp, is_this_your_device_screen=False)

def test_d(self):
"""
Expand Down Expand Up @@ -81,7 +81,7 @@ def test_e(self):
with self.utils.using_test_sp(sp):
self.browser.get(self.sp_shib_url(sp, append='mfaforce'))
self.log_in_netid(self.browser, self.netid, assert_success=False)
self.enter_duo_passcode(self.browser, match_service_provider=sp)
self.enter_duo_passcode(self.browser, match_service_provider=sp, is_this_your_device_screen=False)

def test_f(self):
"""
Expand Down
2 changes: 1 addition & 1 deletion tests/access_control/test_cond2fa_cond_nonmember_both.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ def test_e(self):
with self.utils.using_test_sp(sp):
self.browser.get(self.sp_shib_url(sp, append='mfaforce'))
self.log_in_netid(self.browser, self.netid, assert_success=False)
self.enter_duo_passcode(self.browser, assert_success=False)
self.enter_duo_passcode(self.browser, assert_success=False, is_this_your_device_screen=False)
self.browser.switch_to.default_content()
self.browser.wait_for_tag('p', 'You are not authorized to access the application:')

Expand Down
4 changes: 2 additions & 2 deletions tests/access_control/test_cond2fa_group_member.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def test_c(self):
with self.utils.using_test_sp(sp):
self.browser.get(self.sp_shib_url(sp, append='force'))
self.log_in_netid(self.browser, self.netid, assert_success=False)
self.enter_duo_passcode(self.browser, match_service_provider=sp)
self.enter_duo_passcode(self.browser, match_service_provider=sp, is_this_your_device_screen=False)

def test_d(self):
"""
Expand Down Expand Up @@ -81,7 +81,7 @@ def test_e(self):
with self.utils.using_test_sp(sp):
self.browser.get(self.sp_shib_url(sp, append='mfaforce'))
self.log_in_netid(self.browser, self.netid, assert_success=False)
self.enter_duo_passcode(self.browser, match_service_provider=sp)
self.enter_duo_passcode(self.browser, match_service_provider=sp, is_this_your_device_screen=False)

def test_f(self):
"""
Expand Down
2 changes: 1 addition & 1 deletion tests/access_control/test_cond2fa_non_member.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ def test_e(self):
with self.utils.using_test_sp(sp):
self.browser.get(self.sp_shib_url(sp, append='mfaforce'))
self.log_in_netid(self.browser, self.netid, assert_success=False)
self.enter_duo_passcode(self.browser, match_service_provider=sp)
self.enter_duo_passcode(self.browser, match_service_provider=sp, is_this_your_device_screen=False)

def test_f(self):
"""
Expand Down
2 changes: 1 addition & 1 deletion tests/access_control/test_cond_access_non_member.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,6 @@ def test_e(self):
with self.utils.using_test_sp(sp):
self.browser.get(self.sp_shib_url(sp, append='mfaforce'))
self.log_in_netid(self.browser, self.netid, assert_success=False)
self.enter_duo_passcode(self.browser, assert_success=False)
self.enter_duo_passcode(self.browser, assert_success=False, is_this_your_device_screen=False)
self.browser.switch_to.default_content()
self.browser.wait_for_tag('p', 'You are not authorized to access the application:')
66 changes: 48 additions & 18 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,23 @@ def netid10() -> str:
return AccountNetid.sptest10.value


def duo_push(current_browser: Chrome):
"""
Select the other duo option, to get to the bypass code option
"""
wait = WebDriverWait(current_browser, 10)
wait.until(EC.element_to_be_clickable((By.XPATH, "//a[contains(text(), 'Other options')]")))
current_browser.wait_for_tag('a', 'Other options').click()
current_browser.wait_for_tag('b', 'Other options to log in')


def clear_passcode(current_browser: Chrome, element):
"""
clear the data in the passcode field so you can try a different passcode
"""
current_browser.execute_script("arguments[0].value = '';", element)


@pytest.fixture
def enter_duo_passcode(secrets, sp_domain, test_env) -> Callable[..., NoReturn]:
default_passcode = secrets.test_accounts.duo_code.get_secret_value()
Expand All @@ -180,9 +197,12 @@ def inner(
assert_success: Optional[bool] = None,
assert_failure: Optional[bool] = None,
match_service_provider: Optional[ServiceProviderInstance] = None,
retry: Optional[bool] = False
retry: Optional[bool] = False,
is_this_your_device_screen: Optional[bool] = True,
select_this_is_my_device: Optional[bool] = False,
):
"""
eval and prod flows have 2 different expectations, since duo is updated on eval only, prod to come soon.
:param current_browser: The browser you want to invoke these actions on.
:param passcode: The passcode you want to enter; if not provided, will use the
correct test instance passcode.
Expand All @@ -207,8 +227,12 @@ def inner(
(Providing this gives your tests a higher confidence.)
:param retry: Optional. Tells the function if we are retrying the passcode after entering one that does
not match.
:param is_this_your_device_screen: Optional. Defaults to True, since in most cases we will see the Duo screen that asks
"is this your device". In a small number of cases, you won't see this
screen, ex: an auto 2fa where the user already has given an answer prior
and then switches diafines.
:param select_this_is_my_device: Optional. Defaults to False, since in most cases we don't want duo.com to set a rememberme style cookie.
"""

passcode_matches_default = passcode == default_passcode

if assert_success is None:
Expand All @@ -231,37 +255,43 @@ def inner(
)
current_browser.snap()
current_browser.execute_script("document.getElementById('passcode').click();")
sleep(5)
sleep(5) # this sleep will go away after we copy the universal prompt 2fa to prod.

if select_duo_push and test_env == 'eval':
wait = WebDriverWait(current_browser, 10)
wait.until(EC.element_to_be_clickable((By.XPATH, "//a[contains(text(), 'Other options')]")))
current_browser.wait_for_tag('a', 'Other options').click()
current_browser.wait_for_tag('b', 'Other options to log in')
duo_push(current_browser)

if test_env == 'eval':
# focus on the correct element based on if we are retrying the passcode,
# or its the first time we enter the passcode
wait = WebDriverWait(current_browser, 10)
if assert_success and retry:
if retry:
element = wait.until(EC.element_to_be_clickable((By.XPATH,
"//input[contains(@id, 'passcode-input')]")))
else:
element = wait.until(EC.visibility_of_element_located((By.XPATH, "//div[contains(text(), 'Bypass code') "
"and "
"contains( "
"@class, 'row') and contains(@class, "
"'display-flex') ]"))
)
element = wait.until(EC.visibility_of_element_located((By.XPATH,
"//div[contains(text(), 'Bypass code') and "
"contains(@class, 'row') and contains(@class, "
"'display-flex')]")))

current_browser.snap()
element.click()
current_browser.snap()
if assert_success and retry:
current_browser.execute_script("arguments[0].value = '';", element)
current_browser.snap()
if retry:
clear_passcode(current_browser, element)
current_browser.send_inputs(passcode)
current_browser.snap()
current_browser.snap()
current_browser.wait_for_tag('button', 'Verify').click()

if test_env == 'eval' and is_this_your_device_screen:
if select_this_is_my_device:
element = wait.until(EC.element_to_be_clickable((By.XPATH, "//button[@id='trust-browser-button']")))
else:
element = wait.until(
EC.element_to_be_clickable((By.XPATH, "//button[@id='dont-trust-browser-button' "
"and text()='No, other people use this "
"device']")))

element.click()
current_browser.snap()

if assert_success:
Expand Down
42 changes: 24 additions & 18 deletions tests/test_2fa_duo.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@
2FA-1 thru 2FA-11. 2FA-8b and 2FA-10 are not yet automatable.
"""
from time import sleep

from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from webdriver_recorder.browser import Chrome
from tests.helpers import Locators
from tests.models import ServiceProviderInstance
Expand Down Expand Up @@ -89,7 +94,7 @@ def test_new_session_2fa_invalid_token_retry(self, log_in_netid):
log_in_netid(self.browser, self.netid, assert_success=False)
self.enter_duo_passcode(self.browser,
passcode=passcode,
assert_failure=True)
assert_failure=True, is_this_your_device_screen=False)
self.enter_duo_passcode(self.browser, select_duo_push=False, match_service_provider=sp, assert_success=True, retry=True)

def test_forced_reauth_2fa(self):
Expand All @@ -99,7 +104,7 @@ def test_forced_reauth_2fa(self):
self.browser.get(self.sp_shib_url(sp, append='mfaforce'))
self.browser.send_inputs(self.netid, self.password)
self.browser.click(Locators.submit_button)
self.enter_duo_passcode(self.browser, match_service_provider=sp)
self.enter_duo_passcode(self.browser, match_service_provider=sp, is_this_your_device_screen=False)


def test_2fa_user_opt_in(utils, sp_shib_url, sp_domain, secrets, netid4, test_env, fresh_browser, enter_duo_passcode):
Expand Down Expand Up @@ -142,7 +147,6 @@ def initialize(self, utils, sp_shib_url, sp_domain, secrets, test_env, fresh_bro
self.sp = ServiceProviderInstance.diafine6
self.shib_mfa_url = sp_shib_url(self.sp, append='mfa')

@pytest.mark.usefixtures('skip_if_eval')
def test_2fa_session_single_crn(self, netid2, enter_duo_passcode):
"""
2FA-8 part a. 2FA session using an acct with a Single CRN.
Expand All @@ -152,10 +156,8 @@ def test_2fa_session_single_crn(self, netid2, enter_duo_passcode):
self.browser.get(self.shib_mfa_url)
self.browser.send_inputs(netid2, self.password)
self.browser.click(Locators.submit_button)
self.browser.wait_for_tag('p', "Using your 'sptest03' Duo identity.")
enter_duo_passcode(self.browser, match_service_provider=self.sp)

@pytest.mark.usefixtures('skip_if_eval')
def test_2fa_session_multiple_crn(self, netid10, enter_duo_passcode):
"""
2FA-8 part b. 2FA session using an acct with multiple CRNs.
Expand All @@ -167,13 +169,10 @@ def test_2fa_session_multiple_crn(self, netid10, enter_duo_passcode):
self.browser.click(Locators.submit_button)
self.browser.wait_for_tag('div', 'Select a UW NetID for 2nd factor authentication.')
self.browser.find_element_by_xpath("//input[@value='sptest07']").click()
self.browser.snap()
self.browser.click(Locators.submit_button)
self.browser.wait_for_tag('p', "Using your 'sptest07' Duo identity.")
enter_duo_passcode(self.browser, match_service_provider=self.sp)


@pytest.mark.usefixtures('skip_if_eval')
def test_remember_me_cookie(
utils, sp_shib_url, sp_url, log_in_netid,
sp_domain, secrets, netid3, test_env, fresh_browser, enter_duo_passcode):
Expand All @@ -192,30 +191,37 @@ def test_remember_me_cookie(
fresh_browser.get(sp_shib_url(sp, append='mfa'))
fresh_browser.send_inputs(netid3, password)
fresh_browser.click(Locators.submit_button)
fresh_browser.wait_for_tag('p', 'Use your 2FA device.')
fresh_browser.find_element_by_name('rememberme').click()
enter_duo_passcode(fresh_browser, match_service_provider=sp)
# go to an idp site to retrieve the rememberme cookie
if test_env == 'prod':
fresh_browser.wait_for_tag('p', 'Use your 2FA device.')
fresh_browser.find_element_by_name('rememberme').click()
enter_duo_passcode(fresh_browser, match_service_provider=sp)

if test_env == 'eval':
"""
Select the other duo option, to get to the bypass code option
"""
wait = WebDriverWait(fresh_browser, 10)
wait.until(EC.element_to_be_clickable((By.XPATH, "//a[contains(text(), 'Other options')]")))
enter_duo_passcode(fresh_browser, match_service_provider=sp, select_this_is_my_device=True)

# go to an idp site to retrieve the shib idp cookies
fresh_browser.get(idp_url)
fresh_browser.wait_for_tag('h1', 'not found')

# look for these cookies:
# shib_idp_session, shib_idp_session_ss, and uw-rememberme-sptest03
# shib_idp_session, shib_idp_session_ss

cookie_names = {cookie['name'] for cookie in fresh_browser.get_cookies()}
assert 'shib_idp_session' in cookie_names
assert 'shib_idp_session_ss' in cookie_names
assert 'uw-rememberme-sptest03' in cookie_names

fresh_browser.get(f'{sp_url(sp)}/Shibboleth.sso/Logout?return={idp_url}/profile/Logout')
# After signing out of the IdP, the rememberme cookie should remain in the browser,
# but shib_idp_session* cookies should be gone.
# shib_idp_session* cookies should be gone.
fresh_browser.wait_for_tag('span', 'Your UW NetID sign-in session has ended.')

cookie_names = {cookie['name'] for cookie in fresh_browser.get_cookies()}
assert 'shib_idp_session' not in cookie_names
assert 'shib_idp_session_ss' not in cookie_names
assert 'uw-rememberme-sptest03' in cookie_names

sp = ServiceProviderInstance.diafine12
with utils.using_test_sp(sp):
Expand All @@ -229,7 +235,7 @@ def test_forget_me_self_service(utils, sp_url, sp_domain, secrets, netid3, test_
"""
2FA-11 Forget me (self-service)
Only runs when running tests against prod, for now. Eval does not yet support the uw-rememberme cookie.
Only runs when running tests against prod, for now. Eval does not yet support a forget_me type of function.
"""
idp_env = ''
if test_env == "eval":
Expand Down

0 comments on commit 913e16e

Please sign in to comment.