diff --git a/Graphical/local-user-graphical-login.py b/Graphical/local-user-graphical-login.py index 9894c39..edbec65 100644 --- a/Graphical/local-user-graphical-login.py +++ b/Graphical/local-user-graphical-login.py @@ -31,6 +31,7 @@ from SCAutolib.models.log import assert_log import pytest from time import sleep +from conftest import check_multicert SECURE_LOG = '/var/log/secure' @@ -56,19 +57,20 @@ def test_login_with_sc(local_user, required): expected_log = ( r'.* gdm-smartcard\]\[[0-9]+\]: ' r'pam_sss\(gdm-smartcard:auth\): authentication success;' - r'.*user=' + local_user.username + r'@shadowutils.*' + rf'.*user=({local_user.username}@shadowutils)?.*' ) - with (GUI() as gui, - Authselect(required=required), local_user.card(insert=True)): - gui.assert_text('PIN', timeout=60) - gui.kb_write(local_user.pin) + with (GUI(wait_time=10) as gui, Authselect(required=required)): + for i in range(local_user.total_cards): + with getattr(local_user, f"card_{i}")(insert=True) as sc: + check_multicert(gui=gui) + gui.assert_text('PIN', timeout=60) - with assert_log(SECURE_LOG, expected_log): - gui.kb_send('enter', wait_time=20) - # Mandatory wait to switch display from GDM to GNOME - # Not waiting can actually mess up the output - gui.check_home_screen() + with assert_log(SECURE_LOG, expected_log): + gui.kb_write(sc.pin) + # Mandatory wait to switch display from GDM to GNOME + # Not waiting can actually mess up the output + gui.check_home_screen() @pytest.mark.parametrize("required", [(True), (False)]) @@ -91,20 +93,24 @@ def test_login_with_sc_wrong(local_user, required): expected_log = ( r'.* gdm-smartcard\]\[[0-9]+\]: ' r'pam_sss\(gdm-smartcard:auth\): authentication failure;' - r'.*user=' + local_user.username + r'@shadowutils.*' + rf'.*user=({local_user.username}@shadowutils)?.*' ) - with (GUI() as gui, - Authselect(required=required), local_user.card(insert=True)): - gui.assert_text('PIN', timeout=20) - gui.kb_write(local_user.pin[:-1]) + with (GUI(wait_time=10) as gui, Authselect(required=required)): + for i in range(local_user.total_cards): + with getattr(local_user, f"card_{i}")(insert=True) as sc: + multicert = check_multicert(gui=gui) + gui.assert_text('PIN', timeout=20) - with assert_log(SECURE_LOG, expected_log): - gui.kb_send('enter', wait_time=20) - # Mandatory wait to switch display from GDM to GNOME - # Not waiting can actually mess up the output - gui.check_home_screen(False) - gui.assert_text('PIN', timeout=20) + with assert_log(SECURE_LOG, expected_log): + gui.kb_write(sc.pin[:-1]) + # Mandatory wait to switch display from GDM to GNOME + # Not waiting can actually mess up the output + gui.check_home_screen(False) + if multicert: + gui.assert_text('certificate', timeout=20) + else: + gui.assert_text('PIN', timeout=20) def test_login_password(local_user): @@ -123,13 +129,12 @@ def test_login_password(local_user): """ expected_log = ( r'.* pam_unix\(gdm-password:session\): session opened for user .*' - ) + ) - with GUI() as gui, Authselect(required=False): + with GUI(wait_time=10) as gui, Authselect(required=False): gui.click_on(local_user.username) - gui.kb_write(local_user.password) with assert_log(SECURE_LOG, expected_log): - gui.kb_send('enter', wait_time=20) + gui.kb_write(local_user.password) gui.check_home_screen() @@ -151,14 +156,13 @@ def test_login_password_wrong(local_user): expected_log = ( r'.* gdm-password\]\[[0-9]+\]: ' r'pam_unix\(gdm-password:auth\): authentication failure;' - r'.*user=' + local_user.username + r'.*' + rf'.*user=({local_user.username}@shadowutils)?.*' ) - with GUI() as gui, Authselect(required=False): + with GUI(wait_time=10) as gui, Authselect(required=False): gui.click_on(local_user.username) - gui.kb_write(local_user.password[:-1]) with assert_log(SECURE_LOG, expected_log): - gui.kb_send('enter', wait_time=20) + gui.kb_write(local_user.password[:-1]) gui.check_home_screen(False) gui.assert_text('Password', timeout=20) @@ -182,28 +186,29 @@ def test_insert_card_prompt(local_user, lock_on_removal): C. GDM shows "insert PIN" prompt D. User is logged in successfully. """ - with (GUI() as gui, - Authselect(required=True, lock_on_removal=lock_on_removal), - local_user.card(insert=False) as card): - try: - gui.assert_text('insert', timeout=20) - except Exception: - gui.click_on(local_user.username) - - gui.assert_text('insert', timeout=20) - card.insert() - sleep(10) - gui.assert_text('PIN') - gui.kb_write(local_user.pin) - - expected_log = ( - r'.* gdm-smartcard\]\[[0-9]+\]: ' - r'pam_sss\(gdm-smartcard:auth\): authentication success;' - r'.*user=' + local_user.username + r'(@shadowutils)?.*' - ) - - with assert_log(SECURE_LOG, expected_log): - gui.kb_send('enter', wait_time=20) - # Mandatory wait to switch display from GDM to GNOME - # Not waiting can actually mess up the output - gui.check_home_screen() + with (GUI(wait_time=10) as gui, + Authselect(required=True, lock_on_removal=lock_on_removal)): + for i in range(local_user.total_cards): + with getattr(local_user, f"card_{i}")(insert=True) as sc: + try: + gui.assert_text('insert', timeout=20) + except Exception: + gui.click_on(local_user.username) + + gui.assert_text('insert', timeout=20) + sc.insert() + sleep(10) + check_multicert(gui=gui) + gui.assert_text('PIN') + + expected_log = ( + r'.* gdm-smartcard\]\[[0-9]+\]: ' + r'pam_sss\(gdm-smartcard:auth\): authentication success;' + rf'.*user=({local_user.username}@shadowutils)?.*' + ) + + with assert_log(SECURE_LOG, expected_log): + gui.kb_write(sc.pin) + # Mandatory wait to switch display from GDM to GNOME + # Not waiting can actually mess up the output + gui.check_home_screen() diff --git a/Graphical/local-user-lock-on-removal.py b/Graphical/local-user-lock-on-removal.py index fca318a..2aaec55 100644 --- a/Graphical/local-user-lock-on-removal.py +++ b/Graphical/local-user-lock-on-removal.py @@ -28,6 +28,7 @@ from SCAutolib.models.authselect import Authselect from SCAutolib.models.gui import GUI, keyboard from time import sleep +from conftest import check_multicert import pytest @@ -50,36 +51,39 @@ def test_lock_on_removal(local_user, required): C. The system locks itself after the card is removed D. The system is unlocked """ - with (GUI() as gui, Authselect(required=required, lock_on_removal=True)): + with (GUI(wait_time=10) as gui, + Authselect(required=required, lock_on_removal=True)): # insert the card and sign in a standard way - with local_user.card(insert=True) as card: - sleep(5) - gui.assert_text('PIN', timeout=20) - gui.kb_write(local_user.pin) - gui.kb_send('enter', wait_time=20) - # confirm that you are logged in - gui.check_home_screen() - - # remove the card and wait for the screen to lock - card.remove() - sleep(5) - # Locking the screen in GNOME apparently does not generate any log. - # This could be checked by monitoring D-Bus signals - - # Wake up the black screen by pressing enter - gui.kb_send('enter', screenshot=False) - # Confirm that the screen is locked - # After the screen has been locked, there should be no Activities - gui.check_home_screen(False) - gui.assert_text('insert', timeout=20) - - card.insert() - # click on the password field - gui.click_on('PIN', check_difference=False) - gui.kb_write(local_user.pin) - gui.kb_send('enter', wait_time=20) - # confirm that you are logged back in - gui.check_home_screen() + + for i in range(local_user.total_cards): + with getattr(local_user, f"card_{i}")(insert=True) as sc: + sleep(5) + check_multicert(gui=gui) + gui.assert_text('PIN', timeout=20) + gui.kb_write(sc.pin) + # confirm that you are logged in + gui.check_home_screen() + + # remove the card and wait for the screen to lock + sc.remove() + sleep(5) + # Locking the screen in GNOME apparently does not generate any log. + # This could be checked by monitoring D-Bus signals + + # Wake up the black screen by pressing enter + gui.kb_send('enter', screenshot=False) + # Confirm that the screen is locked + # After the screen has been locked, there should be no Activities + gui.check_home_screen(False) + gui.assert_text('insert', timeout=20) + + sc.insert() + check_multicert(gui=gui) + # click on the password field + gui.click_on('PIN') + gui.kb_write(sc.pin) + # confirm that you are logged back in + gui.check_home_screen() def test_lock_on_removal_password(local_user): @@ -99,24 +103,25 @@ def test_lock_on_removal_password(local_user): C. Nothing happens D. Nothing happens - system will not lock on card removal """ - with (GUI() as gui, Authselect(required=False, lock_on_removal=True)): - with local_user.card(insert=False) as card: - gui.click_on(local_user.username) - gui.kb_write(local_user.password) - gui.kb_send('enter', wait_time=20) - gui.check_home_screen() + with (GUI(wait_time=10) as gui, + Authselect(required=False, lock_on_removal=True)): + for i in range(local_user.total_cards): + with getattr(local_user, f"card_{i}")() as sc: + gui.click_on(local_user.username) + gui.kb_write(local_user.password) + gui.check_home_screen() - card.insert() - sleep(10) - card.remove() - sleep(10) + sc.insert() + sleep(10) + sc.remove() + sleep(10) - # Screen should be unlocked - gui.check_home_screen() + # Screen should be unlocked + gui.check_home_screen() @pytest.mark.parametrize("lock_on_removal", [(True), (False)]) -def test_lockscreen_password(local_user, lock_on_removal): +def test_lockscreen_password(local_user, check_multicert, lock_on_removal): """Local user unlocks screen using password, even if the smart card is inserted (after the password login). Screen unlocking requires the same method (PIN vs password) as was used for login. @@ -137,31 +142,30 @@ def test_lockscreen_password(local_user, lock_on_removal): D. The screen is locked E. Screen is unlocked successfully """ - with (GUI() as gui, - Authselect(required=False, lock_on_removal=lock_on_removal), - local_user.card(insert=False) as card): - gui.click_on(local_user.username) - gui.kb_write(local_user.password) - gui.kb_send('enter', wait_time=20) - gui.check_home_screen() - - card.insert() - sleep(10) - # press shortcut to lock the screen - # keyboard.send('windows+l') cannot be parsed properly - # this is a workaround for keyboard library - keyboard.press((125, 126),) - keyboard.send('l') - keyboard.release((125, 126),) - sleep(10) - - # Wake up the black screen by pressing enter - gui.kb_send('enter', screenshot=False) - # Confirm that the screen is locked - # After the screen has been locked, there should be no Activities - gui.check_home_screen(False) - gui.click_on('Password', check_difference=False) - gui.kb_write(local_user.password) - gui.kb_send('enter', wait_time=10) - # confirm that you are logged back in - gui.check_home_screen() + with (GUI(wait_time=10) as gui, + Authselect(required=False, lock_on_removal=lock_on_removal)): + for i in range(local_user.total_cards): + with getattr(local_user, f"card_{i}")() as sc: + gui.click_on(local_user.username) + gui.kb_write(local_user.password) + gui.check_home_screen() + + sc.insert() + sleep(10) + # press shortcut to lock the screen + # keyboard.send('windows+l') cannot be parsed properly + # this is a workaround for keyboard library + keyboard.press((125, 126),) + keyboard.send('l') + keyboard.release((125, 126),) + sleep(10) + + # Wake up the black screen by pressing enter + gui.kb_send('enter', screenshot=False) + # Confirm that the screen is locked + # After the screen has been locked, there should be no Activities + gui.check_home_screen(False) + gui.click_on('Password', check_difference=False) + gui.kb_write(local_user.password) + # confirm that you are logged back in + gui.check_home_screen() diff --git a/Local-user/test_local_user_login.py b/Local-user/test_local_user_login.py index 564b3d3..8d06b1e 100644 --- a/Local-user/test_local_user_login.py +++ b/Local-user/test_local_user_login.py @@ -1,10 +1,11 @@ """Note: as all tests are executed from root user, first login to any user do not require any credentials! """ +import re import pytest +from conftest import check_multicert from SCAutolib.models.authselect import Authselect - @pytest.mark.parametrize("required", [True, False]) def test_su_login_with_sc(local_user, user_shell, required): """Basic su login to the user with a smart card. @@ -41,12 +42,14 @@ def test_su_login_with_sc(local_user, user_shell, required): """ with Authselect(required=required): - with local_user.card(insert=True): - cmd = f'su {local_user.username} -c "whoami"' - user_shell.sendline(cmd) - user_shell.expect_exact(f"PIN for {local_user.username}:") - user_shell.sendline(local_user.pin) - user_shell.expect_exact(local_user.username) + for i in range(local_user.total_cards): + with getattr(local_user, f"card_{i}")(insert=True) as sc: + cmd = f'su {local_user.username} -c "whoami"' + user_shell.sendline(cmd) + check_multicert(shell=user_shell) + user_shell.expect(f"PIN for.*{re.escape(sc.label)}.*:") + user_shell.sendline(sc.pin) + user_shell.expect_exact(local_user.username) @pytest.mark.parametrize("required", [True, False]) @@ -84,12 +87,14 @@ def test_su_login_with_sc_wrong(local_user, user_shell, required): - User is not logged in and error message is written to the console """ with Authselect(required=required): - with local_user.card(insert=True): - cmd = f'su {local_user.username} -c "whoami"' - user_shell.sendline(cmd) - user_shell.expect_exact(f"PIN for {local_user.username}:") - user_shell.sendline("wrong") - user_shell.expect(f"su: Authentication failure") + for i in range(local_user.total_cards): + with getattr(local_user, f"card_{i}")(insert=True) as sc: + cmd = f'su {local_user.username} -c "whoami"' + user_shell.sendline(cmd) + check_multicert(shell=user_shell) + user_shell.expect(f"PIN for.*{re.escape(sc.label)}.*:") + user_shell.sendline(sc.pin + "extra") + user_shell.expect(f"su: Authentication failure") def test_gdm_login_sc_required(local_user, root_shell): @@ -128,14 +133,18 @@ def test_gdm_login_sc_required(local_user, root_shell): """ with Authselect(required=True): - with local_user.card as sc: - cmd = f'sssctl user-checks -s gdm-smartcard {local_user.username} -a auth' - root_shell.sendline(cmd) - root_shell.expect_exact("Please insert smart card") - sc.insert() - root_shell.expect_exact(f"PIN for {local_user.username}") - root_shell.sendline(local_user.pin) - root_shell.expect("pam_authenticate.*Success") + for i in range(local_user.total_cards): + with getattr(local_user, f"card_{i}") as sc: + cmd = f'sssctl user-checks -s gdm-smartcard {local_user.username} -a auth' + root_shell.sendline(cmd) + root_shell.expect_exact("Please insert smart card") + + sc.insert() + + check_multicert(shell=root_shell) + root_shell.expect(f"PIN for.*{re.escape(sc.label)}.*:") + root_shell.sendline(sc.pin) + root_shell.expect("pam_authenticate.*Success") def test_su_login_without_sc(local_user, user_shell): @@ -213,14 +222,16 @@ def test_su_to_root(local_user, user_shell, root_user, required, lock_on_removal - User is switched to the root user """ with Authselect(required=required, lock_on_removal=lock_on_removal): - with local_user.card(insert=True): - user_shell.sendline(f"su - {local_user.username}") - user_shell.expect_exact(f"PIN for {local_user.username}:") - user_shell.sendline(local_user.pin) - user_shell.expect_exact(local_user.username) - user_shell.sendline("whoami") - user_shell.expect_exact(local_user.username) - user_shell.sendline('su - root -c "whoami"') - user_shell.expect_exact("Password:") - user_shell.sendline(root_user.password) - user_shell.expect_exact("root") + for i in range(local_user.total_cards): + with getattr(local_user, f"card_{i}")(insert=True) as sc: + user_shell.sendline(f"su - {local_user.username}") + check_multicert(shell=user_shell) + user_shell.expect(f"PIN for.*{re.escape(sc.label)}.*:") + user_shell.sendline(sc.pin) + user_shell.expect_exact(local_user.username) + user_shell.sendline("whoami") + user_shell.expect_exact(local_user.username) + user_shell.sendline('su - root -c "whoami"') + user_shell.expect_exact("Password:") + user_shell.sendline(root_user.password) + user_shell.expect_exact("root") diff --git a/Sanity/test_certs.py b/Sanity/test_certs.py index 24f47b2..f2ffb48 100644 --- a/Sanity/test_certs.py +++ b/Sanity/test_certs.py @@ -33,7 +33,7 @@ def test_wrong_issuer_cert(local_user, sssd_db, user_shell, tmp_path): sssd_db.path.unlink() run(['mkdir', tmp_path.joinpath("ca")]) - ca = BaseCA.factory(path=tmp_path.joinpath("ca"), create=True) + BaseCA.factory(path=tmp_path.joinpath("ca"), create=True) run(['restorecon', "-v", "/etc/sssd/pki/sssd_auth_ca_db.pem"]) with Authselect(): diff --git a/Sanity/test_smart_card_detection.py b/Sanity/test_smart_card_detection.py index 4798bdc..b2c7614 100644 --- a/Sanity/test_smart_card_detection.py +++ b/Sanity/test_smart_card_detection.py @@ -1,7 +1,3 @@ -import pytest -from time import sleep -import pexpect - def test_modutil_token_info(local_user, root_shell): """Check that p11-kit module shows smart card information with modutil command""" @@ -44,3 +40,14 @@ def test_pam_services_config(local_user, root_shell, sssd): root_shell.sendline(local_user.pin) root_shell.expect_exact(f"pam_authenticate for user " f"[{local_user.username}]: Success") + + +def test_physical_card_detection(local_user, root_shell): + for i in range(local_user.total_cards): + with getattr(local_user, f"card_{i}") as sc: + cmd = "pkcs11-tool -L" + root_shell.sendline(cmd) + root_shell.expect_exact("(empty)") + sc.insert() + root_shell.sendline(cmd) + root_shell.expect_exact(sc.label) diff --git a/Sanity/test_ttylogin.py b/Sanity/test_ttylogin.py index 320c90f..0f920da 100644 --- a/Sanity/test_ttylogin.py +++ b/Sanity/test_ttylogin.py @@ -7,8 +7,10 @@ that TTY. Therefore, execution of login command in nearly the same way agetty does it is good approximation to manual testing in virtual console. """ +import re import sys from time import sleep +from conftest import check_multicert import pexpect import pytest @@ -348,17 +350,19 @@ def test_login_local_su_to_root(user, root_user, required, lock_on_removal): - User is switched to the root user """ with Authselect(required=required, lock_on_removal=lock_on_removal): - with user.card(insert=True): - login_shell = login_shell_factory(user.username) - login_shell.expect([f"PIN for {user.username}:"]) - login_shell.sendline(user.pin) - login_shell.expect([user.username]) - login_shell.sendline("whoami") - login_shell.expect_exact(user.username) - login_shell.sendline('su - root -c "whoami"') - login_shell.expect_exact("Password:") - login_shell.sendline(root_user.password) - login_shell.expect_exact("root") + for i in range(user.total_cards): + with getattr(user, f"card_{i}")(insert=True) as sc: + login_shell = login_shell_factory(user.username) + check_multicert(shell=login_shell) + login_shell.expect([f"PIN for.*{re.escape(sc.label)}.*:"]) + login_shell.sendline(sc.pin) + login_shell.expect([user.username]) + login_shell.sendline("whoami") + login_shell.expect_exact(user.username) + login_shell.sendline('su - root -c "whoami"') + login_shell.expect_exact("Password:") + login_shell.sendline(root_user.password) + login_shell.expect_exact("root") @pytest.mark.parametrize("required", [True, False]) def test_login_kerberos_su_to_root(ipa_user, root_user, required): diff --git a/conftest.py b/conftest.py index 53e8f24..610b999 100644 --- a/conftest.py +++ b/conftest.py @@ -12,27 +12,33 @@ ipa_server = None local_user = None tokens = None +multicert = None def load_tokens(user, token_list, update_sssd): log.info("Loading tokens") + user.total_cards = len(token_list) for index, token in enumerate(token_list): log.debug("Loading %s. token", index) setattr( user, f"card_{index}", - Card.load(card_name = token, update_sssd = update_sssd) + Card.load(card_name=token, update_sssd=update_sssd) ) log.debug(f"Token %s is loaded", index) -def update_ca(user, token_list): - log.info("Loading local CA") - for index, _ in enumerate(token_list): - card_name = f"card_{index}" - card = getattr(user, card_name, None) - ca = BaseCA.factory(ca_name = card.ca_name) - ca.update_ca_db() - log.debug("CA database is updated") +def check_multicert(shell = None, gui = None): + global multicert + if multicert: + # If the card has multiple certs and you need to choose + if shell: + shell.expect_exact(f"select a certificate") + shell.sendline(multicert) + if gui: + gui.assert_text('select a certificate', timeout=10) + gui.click_on("Certificate for", click_on_match=int(multicert)) + return True + return False def pytest_configure(config): @@ -40,8 +46,10 @@ def pytest_configure(config): global ipa_server global local_user global tokens + global multicert user_type = config.getoption("user_type") tokens = config.getoption("tokens") + multicert = config.getoption("select_cert") # workaround to set default token as parser.addoption defining tokens # is a list that needs to be empty by default @@ -58,7 +66,7 @@ def pytest_configure(config): ipa_server=ipa_server) assert ipa_user.user_type == "ipa" log.debug("IPA user is loaded") - load_tokens(ipa_user, tokens, config.getoption("keep_sssd")) + load_tokens(ipa_user, tokens, config.getoption("update_sssd")) ipa_user.card = ipa_user.card_0 ipa_user.pin = ipa_user.card.pin if user_type in ["local", "all"]: @@ -66,7 +74,7 @@ def pytest_configure(config): local_user = User.load(username = config.getoption("local_username")) assert local_user.user_type == "local" log.debug("Local user is loaded") - load_tokens(local_user, tokens, config.getoption("keep_sssd")) + load_tokens(local_user, tokens, config.getoption("update_sssd")) # backwards compatibility fix. Older tests expected one virtual card # as attribute of user - i.e. user.card and approached card this way. # As of now we expect user can have multiple cards, they are marked @@ -78,8 +86,6 @@ def pytest_configure(config): # pin was moved to card. For backwards compatibility: local_user.pin = local_user.card.pin - update_ca(local_user, tokens) - def pytest_addoption(parser): """ @@ -115,10 +121,20 @@ def pytest_addoption(parser): help="List of tokens to be prepared" ) parser.addoption( - "--keep-sssd", - action="store_false", - dest="keep_sssd", - help="Prevents the forced change of sssd.conf" + "--update-sssd", + action="store_true", + default=False, + dest="update_sssd", + help="Force change of sssd.conf" + ) + parser.addoption( + "--select-cert", + action="store", + default=None, + dest="select_cert", + help="Use if card has multiple certs stored. " + "Provide which cert to use. " + "Note that this selection will apply to all tokens!" ) @@ -160,3 +176,5 @@ def pytest_generate_tests(metafunc): metafunc.parametrize("ipa_server", [ipa_server]) if "tokens" in metafunc.fixturenames: metafunc.parametrize("tokens", [tokens]) + if "check_multicert" in metafunc.fixturenames: + metafunc.parametrize("check_multicert", [check_multicert]) diff --git a/fixtures.py b/fixtures.py index 2374e74..0aaab55 100644 --- a/fixtures.py +++ b/fixtures.py @@ -31,25 +31,30 @@ def allow_sudo_commands(ipa_user): """ logger = logging.getLogger() + logger.debug("Checking if the allow_sudo rule is there.") + out = run('ipa sudorule-show allow_sudo'.split(), return_code = [0, 2]) + if out.returncode == 0: + run('ipa sudorule-del allow_sudo'.split()) + run("systemctl restart sssd".split(), sleep = 10) run('ipa sudorule-add allow_sudo --hostcat=all --runasusercat=all --runasgroupcat=all --cmdcat=all'.split()) run(f'ipa sudorule-add-user allow_sudo --user {ipa_user.username}'.split()) - run("systemctl restart sssd".split(), sleep = 5) + run("systemctl restart sssd".split(), sleep = 10) logger.debug("Checking that the sudo rule has been added (following command should succeed)") run('ipa sudorule-show allow_sudo'.split()) yield # running the test's code run('ipa sudorule-del allow_sudo'.split()) - run("systemctl restart sssd".split(), sleep = 5) + run("systemctl restart sssd".split(), sleep = 10) logger.debug("Checking that the sudo rule has been removed (following command should exit with status 2)") run('ipa sudorule-show allow_sudo'.split(), return_code = [2]) @pytest.fixture(scope="session") def root_user(): - return User.load(username = "root") + return User.load(username="root") @pytest.fixture(scope="session") def base_user(): - return User.load(username = "base-user") + return User.load(username="base-user") @pytest.fixture(scope="session")