diff --git a/plugins/modules/login.py b/plugins/modules/login.py index f85ceb5..2e6dcb3 100644 --- a/plugins/modules/login.py +++ b/plugins/modules/login.py @@ -23,7 +23,8 @@ api_2fa: description: - The Uptime Kuma 2FA token. - - Only required if no I(api_token) specified and authentication with 2FA is enabled. + - Only required if authentication with 2FA is enabled. + type: str ''' EXAMPLES = r''' @@ -70,9 +71,11 @@ def run(api, params, result): def main(): - module_args = {} + module_args = dict( + api_2fa=dict(type="str", no_log=True) + ) module_args.update(common_module_args) - module_args.update({"api_2fa": dict(type="str", no_log=True)}) + module_args.pop("api_token") module = AnsibleModule(module_args) params = module.params diff --git a/plugins/modules/settings_info.py b/plugins/modules/settings_info.py index da3541f..3136a06 100644 --- a/plugins/modules/settings_info.py +++ b/plugins/modules/settings_info.py @@ -115,7 +115,7 @@ def run(api, params, result): def main(): - module_args = {} + module_args = dict() module_args.update(common_module_args) module = AnsibleModule(module_args, supports_check_mode=True) diff --git a/plugins/modules/setup.py b/plugins/modules/setup.py index 84ae9a4..a99c306 100644 --- a/plugins/modules/setup.py +++ b/plugins/modules/setup.py @@ -57,7 +57,7 @@ def run(api, params, result): def main(): - module_args = {} + module_args = dict() module_args.update(common_module_args) module = AnsibleModule(module_args) diff --git a/tests/requirements.txt b/tests/requirements.txt index 4ec5763..45183cf 100644 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -1,3 +1,4 @@ pytest pytest-xdist packaging +pyotp diff --git a/tests/unit/plugins/module_utils/test_login.py b/tests/unit/plugins/module_utils/test_login.py index 96247c2..dd96d1d 100644 --- a/tests/unit/plugins/module_utils/test_login.py +++ b/tests/unit/plugins/module_utils/test_login.py @@ -1,7 +1,22 @@ +from urllib import parse + +import pyotp + from .module_test_case import ModuleTestCase import plugins.modules.login as module +def parse_secret(uri): + query = parse.urlsplit(uri).query + params = dict(parse.parse_qsl(query)) + return params["secret"] + + +def generate_token(secret): + totp = pyotp.TOTP(secret) + return totp.now() + + class TestLogin(ModuleTestCase): def setUp(self): super(TestLogin, self).setUp() @@ -9,7 +24,7 @@ def setUp(self): "api_url": "http://127.0.0.1:3001", "api_username": None, "api_password": None, - "api_token": None + "api_2fa": None } def test_login(self): @@ -19,3 +34,31 @@ def test_login(self): result = self.run_module(module, self.params) self.assertFalse(result["changed"]) self.assertIsNotNone(result["token"]) + + def test_login_with_2fa(self): + self.params["api_username"] = self.username + self.params["api_password"] = self.password + + # prepare 2fa + r = self.api.prepare_2fa(self.password) + uri = r["uri"] + secret = parse_secret(uri) + + # verify token + token = generate_token(secret) + r = self.api.verify_token(token, self.password) + self.assertEqual(r["valid"], True) + + # save 2fa + r = self.api.save_2fa(self.password) + self.assertEqual(r["msg"], "2FA Enabled.") + + # run module + self.params["api_2fa"] = token + result = self.run_module(module, self.params) + self.assertFalse(result["changed"]) + self.assertIsNotNone(result["token"]) + + # disable 2fa + r = self.api.disable_2fa(self.password) + self.assertEqual(r["msg"], "2FA Disabled.")