def test_sync(self): with TempConfig(MOCKDUO_CONF) as temp: result = login_duo( ["-d", "-c", temp.name, "-f", "whatever", "true"], ) self.assertRegexpMatches( result["stderr"][0], r"Successful Duo login for 'whatever'", )
def test_su_service_bad_user(self): """Test that we return user unknown if we can't find the calling user""" with TempConfig(MOCKDUO_CONF) as temp: result = pam_duo( ["-d", "-c", temp.name], env={"PAM_SERVICE": "su", "NO_USER": "******"}, ) self.assertEqual(result["returncode"], 1)
def test_gecos_field_unparsed(self): with TempConfig(MOCKDUO_GECOS_SEND_UNPARSED) as temp: result = pam_duo( ["-d", "-c", temp.name, "-f", "fullgecos", "true"], ) self.assertRegexpMatches( result["stderr"][0], r"Skipped Duo login for 'full_gecos_field': full-gecos-field", )
def test_gecos_delimiter_default_position_6(self): with TempConfig(MOCKDUO_GECOS_DEFAULT_DELIM_6_POS) as temp: result = pam_duo( ["-d", "-c", temp.name, "-f", "gecos,6", "true"], ) self.assertRegexpMatches( result["stderr"][0], "Skipped Duo login for 'gecos_user_gecos_field6': gecos-user-gecos-field6-allowed", )
def test_gecos_delimiter_slash_position_3(self): with TempConfig(MOCKDUO_GECOS_SLASH_DELIM_3_POS) as temp: result = pam_duo( ["-d", "-c", temp.name, "-f", "gecos/3", "true"], ) self.assertRegexpMatches( result["stderr"][0], r"Skipped Duo login for 'gecos_user_gecos_field3': gecos-user-gecos-field3-allowed", )
def test_gecos_delimiter_slash_position_3(self): with TempConfig(MOCKDUO_GECOS_SLASH_DELIM_3_POS) as temp: result = login_duo( ["-d", "-c", temp.name, "true"], env={"UID": "1011"}, preload_script=os.path.join(TESTDIR, "login_duo.py"), ) self.assertRegexpMatches( result["stderr"][0], r"Skipped Duo login for 'gecos_user_gecos_field3': gecos-user-gecos-field3-allowed", )
def test_gecos_empty(self): with TempConfig(MOCKDUO_GECOS_SEND_UNPARSED) as temp: result = login_duo( ["-d", "-c", temp.name, "true"], env={"UID": "1016"}, preload_script=os.path.join(TESTDIR, "login_duo.py"), ) self.assertRegexpMatches( result["stderr"][0], r"Empty GECOS field", )
def test_invalid_argument(self): with TempConfig(MOCKDUO_CONF) as duo_config: pamd_conf = "auth required {libpath}/pam_duo.so conf={duo_config_path} notanarg".format( libpath=os.path.join(topbuilddir, "pam_duo", ".libs"), duo_config_path=duo_config.name, ) with TempPamConfig(pamd_conf) as pam_config: process = testpam( ["-d", "-c", duo_config.name, "-f", "whatever"], pam_config.name ) self.assertEqual(process.returncode, 1)
def test_gecos_parsing_error(self): with TempConfig(MOCKDUO_GECOS_SLASH_DELIM_3_POS) as temp: result = login_duo( ["-d", "-c", temp.name, "true"], env={"UID": "1012"}, preload_script=os.path.join(TESTDIR, "login_duo.py"), ) self.assertRegexpMatches( result["stderr"][0], r"Could not parse GECOS field", )
def test_gecos_field_unparsed(self): with TempConfig(MOCKDUO_GECOS_SEND_UNPARSED) as temp: result = login_duo( ["-d", "-c", temp.name, "true"], env={"UID": "1010"}, preload_script=os.path.join(TESTDIR, "login_duo.py"), ) self.assertRegexpMatches( result["stderr"][0], r"Successful Duo login for '1/2/3/4/5/gecos_user_gecos_field6'", )
def test_shell_as_command(self): with TempConfig(MOCKDUO_AUTOPUSH) as temp: process = login_duo_interactive( ["-d", "-c", temp.name, "echo", "SUCCESS"], env={ "PS1": "> ", "UID": "1017" }, preload_script=os.path.join(TESTDIR, "login_duo.py"), ) self.assertEqual(process.expect("-c echo SUCCESS", timeout=10), 0)
def test_users_bypass(self): with TempConfig(MOCKDUO_USERS) as temp: result = login_duo( ["-d", "-c", temp.name, "-f", "preauth-allow", "true"], env={"UID": "1004"}, preload_script=os.path.join(TESTDIR, "groups.py"), ) self.assertRegexpMatches( result["stderr"][0], r"User preauth-allow bypassed Duo 2FA due to user's UNIX group", )
def test_max_prompts_equals_one(self): with TempConfig(MOCKDUO_PROMPTS_1) as temp: result = pam_duo(["-d", "-f", "pam_prompt", "-c", temp.name, "true"]) self.assertRegexpMatches( result["stderr"][0], "Failed Duo login for 'pam_prompt'" ) self.assertRegexpMatches( result["stdout"][0], "Autopushing login request to phone..." ) self.assertRegexpMatches( result["stdout"][1], "Invalid passcode, please try again." )
def test_deprecated_gecos_parsed_flag(self): with TempConfig(MOCKDUO_GECOS_DEPRECATED_PARSE_FLAG) as temp: result = pam_duo( ["-d", "-c", temp.name, "-f", "gecos/6", "true"], ) self.assertRegexpMatches( result["stderr"][0], r"The gecos_parsed configuration item for Duo Unix is deprecated and no longer has any effect. Use gecos_delim and gecos_username_pos instead", ) self.assertRegexpMatches( result["stderr"][1], "Skipped Duo login for 'gecos/6': gecos/6", )
def test_admins_and_not_users_match_admins(self): with TempConfig(MOCKDUO_ADMINS_NO_USERS) as temp: result = login_duo( ["-d", "-c", temp.name, "-f", "preauth-allow", "true"], env={ "UID": "1003", }, preload_script=os.path.join(TESTDIR, "groups.py"), ) self.assertRegexpMatches( result["stderr"][0], r"Skipped Duo login for 'preauth-allow': preauth-allowed", )
def test_default_shell(self): """Test that we fallback to /bin/sh if there is no shell specified for the user""" with TempConfig(MOCKDUO_AUTOPUSH) as temp: process = login_duo_interactive( ["-d", "-c", temp.name], env={ "PS1": "$ ", "UID": "1015" }, preload_script=os.path.join(TESTDIR, "login_duo.py"), ) # this double escaping is needed to check for a literal "$" self.assertEqual(process.expect("\\$", timeout=10), 0)
def test_missing_uid(self): with TempConfig(MOCKDUO_CONF) as temp: result = login_duo( ["-d", "-c", temp.name, "-f", "timeout", "true"], env={ "TIMEOUT": "1", }, preload_script=os.path.join(TESTDIR, "login_duo.py"), ) self.assertRegexpMatches( result["stderr"][0], r"Who are you?", )
def test_users_only_match_users(self): for uid in range(1000, 1003): with TempConfig(MOCKDUO_USERS) as temp: result = login_duo( ["-d", "-c", temp.name, "-f", "preauth-allow", "true"], env={ "UID": str(uid), }, preload_script=os.path.join(TESTDIR, "groups.py"), ) self.assertRegexpMatches( result["stderr"][0], r"Skipped Duo login for 'preauth-allow': preauth-allowed", )
def test_nonroot(self): with TempConfig(MOCKDUO_CONF) as temp: result = login_duo( ["-d", "-c", temp.name, "-f", "preauth-allow"], env={ "EUID": "1002", "UID": "1001", }, preload_script=os.path.join(TESTDIR, "login_duo.py"), ) self.assertRegexpMatches( result["stderr"][0], r"Only root may specify -c or -f", )
def test_command_from_env(self): with TempConfig(MOCKDUO_CONF) as temp: result = login_duo( ["-d", "-c", temp.name, "-f", "preauth-allow"], env={ "UID": "1001", "SSH_ORIGINAL_COMMAND": "echo 'hello'", }, preload_script=os.path.join(TESTDIR, "login_duo.py"), ) self.assertRegexpMatches( result["stdout"][0], r"hello", )
def test_unprivileged(self): with TempConfig(MOCKDUO_CONF) as temp: result = login_duo( ["-d"], env={ "EUID": "1000", "UID": "1001", }, preload_script=os.path.join(TESTDIR, "login_duo.py"), timeout=10, ) self.assertRegexpMatches( result["stderr"][0], r"couldn't drop privileges:", )
def test_deprecated_gecos_parsed_flag(self): with TempConfig(MOCKDUO_GECOS_DEPRECATED_PARSE_FLAG) as temp: result = login_duo( ["-d", "-c", temp.name, "true"], env={"UID": "1010"}, preload_script=os.path.join(TESTDIR, "login_duo.py"), ) self.assertRegexpMatches( result["stderr"][0], r"The gecos_parsed configuration item for Duo Unix is deprecated and no longer has any effect. Use gecos_delim and gecos_username_pos instead", ) self.assertRegexpMatches( result["stderr"][1], "Skipped Duo login for 'gecos/6': gecos/6", )
def test_invalid_pos_value(self): with TempConfig(MOCKDUO_GECOS_INVALID_POS) as temp: result = login_duo(["-d", "-c", temp.name, "true"], ) self.assertEquals( result["stderr"][0], "Gecos position starts at 1", ) self.assertRegexpMatches( result["stderr"][1], r"Invalid login_duo option: 'gecos_username_pos'", ) self.assertRegexpMatches( result["stderr"][2], r"Parse error in {config}, line \d+".format(config=temp.name), )
def test_motd(self): with TempConfig(MOTD_CONF) as temp: test_motd = "test_string" with open("/tmp/duomotdtest", "w") as fh: fh.write("{motd}\n".format(motd=test_motd)) process = login_duo_interactive( ["-d", "-c", temp.name, "-f", "whatever", "echo", "SUCCESS"], env={ "UID": "1001", }, preload_script=os.path.join(TESTDIR, "login_duo.py"), ) process.sendline("1") self.assertEqual(process.expect("test_string", timeout=10), 0)
def test_invalid_delimiter_value_whitespace(self): with TempConfig(MOCKDUO_GECOS_INVALID_DELIM_WHITESPACE) as temp: result = login_duo(["-d", "-c", temp.name, "true"], ) self.assertEquals( result["stderr"][0], "Invalid character option length. Character fields must be 1 character long: ''", ) self.assertRegexpMatches( result["stderr"][1], r"Invalid login_duo option: 'gecos_delim'", ) self.assertRegexpMatches( result["stderr"][2], r"Parse error in {config}, line \d+".format(config=temp.name), )
def test_gecos_invalid_delimiter_length(self): with TempConfig(MOCKDUO_GECOS_LONG_DELIM) as temp: result = login_duo(["-d", "-c", temp.name, "true"], ) self.assertRegexpMatches( result["stderr"][0], r"Invalid character option length. Character fields must be 1 character long: ',,'", ) self.assertRegexpMatches( result["stderr"][1], r"Invalid login_duo option: 'gecos_delim'", ) self.assertRegexpMatches( result["stderr"][2], r"Parse error in {config}, line \d+".format(config=temp.name), )
def test_privsep_user_not_found(self): with TempConfig(MOCKDUO_CONF) as temp: result = login_duo( ["-d"], env={ "EUID": "0", "UID": "1001", "NO_PRIVSEP_USER": "******", }, preload_script=os.path.join(TESTDIR, "login_duo.py"), timeout=10, ) self.assertRegexpMatches( result["stderr"][0], r"User .* not found", )
def test_connection_timeout(self): with TempConfig(MOCKDUO_CONF) as temp: result = login_duo( ["-d", "-c", temp.name, "-f", "timeout", "true"], env={ "UID": "1001", "TIMEOUT": "1", }, preload_script=os.path.join(TESTDIR, "login_duo.py"), timeout=10, ) for line in result["stderr"][:3]: self.assertEqual(line, "Attempting connection") self.assertRegexpMatches( result["stderr"][3], r"Failsafe Duo login for 'timeout': Couldn't connect to localhost:4443: Failed to connect", )
def test_env_factor(self): config = DuoUnixConfig( ikey="DIXYZV6YM8IFYVWBINCA", skey="yWHSMhWucAcp7qvuH3HWTaSaKABs8Gaddiv1NIRo", host="localhost:4443", cafile="certs/mockduo-ca.pem", accept_env_factor="yes", ) with TempConfig(config) as temp: process = login_duo_interactive( ["-d", "-c", temp.name, "-f", "whatever", "echo", "SUCCESS"], env={ "UID": "1001", "DUO_PASSCODE": "push1", }, ) self.assertEqual(process.expect("SUCCESS", timeout=10), 0)
def test_invalid_delimiter_value(self): for config in [ MOCKDUO_GECOS_INVALID_DELIM_COLON, MOCKDUO_GECOS_INVALID_DELIM_PUNC, ]: with TempConfig(config) as temp: result = login_duo(["-d", "-c", temp.name, "true"], ) self.assertEquals( result["stderr"][0], "Invalid gecos_delim '{delim}' (delimiter must be punctuation other than ':')" .format(delim=config["gecos_delim"]), ) self.assertRegexpMatches( result["stderr"][1], r"Invalid login_duo option: 'gecos_delim'", ) self.assertRegexpMatches( result["stderr"][2], r"Parse error in {config}, line \d+".format( config=temp.name), )