diff --git a/st2client/st2client/base.py b/st2client/st2client/base.py index f90dedb8c0..c1bc62f489 100644 --- a/st2client/st2client/base.py +++ b/st2client/st2client/base.py @@ -124,7 +124,7 @@ def get_client(self, args, debug=False): password = credentials.get('password', None) cache_token = rc_config.get('cli', {}).get('cache_token', False) - if username and password: + if credentials: # Credentials are provided, try to authenticate agaist the API try: token = self._get_auth_token(client=client, username=username, password=password, diff --git a/st2client/st2client/commands/auth.py b/st2client/st2client/commands/auth.py index 1735e7ea11..98a37820c5 100644 --- a/st2client/st2client/commands/auth.py +++ b/st2client/st2client/commands/auth.py @@ -142,12 +142,26 @@ def run(self, args, **kwargs): def run_and_print(self, args, **kwargs): try: self.run(args, **kwargs) - print("Logged in as %s" % (args.username)) except Exception as e: - print("Failed to log in as %s: %s" % (args.username, str(e))) + print('Failed to log in as %s: %s' % (args.username, str(e))) if self.app.client.debug: raise + print('Logged in as %s' % (args.username)) + + if not args.write_password: + # Note: Client can't depend and import from common so we need to hard-code this + # default value + token_expire_hours = 24 + + print('') + print('Note: You didn\'t use --write-password option so the password hasn\'t been ' + 'stored in the client config and you will need to login again in %s hours when ' + 'the auth token expires.' % (token_expire_hours)) + print('As an alternative, you can run st2 login command with the "--write-password" ' + 'flag, but keep it mind this will cause it to store the password in plain-text ' + 'in the client config file (~/.st2/config).') + class WhoamiCommand(resource.ResourceCommand): display_attributes = ['user', 'token', 'expiry'] diff --git a/st2client/st2client/shell.py b/st2client/st2client/shell.py index c26c302984..915cd89612 100755 --- a/st2client/st2client/shell.py +++ b/st2client/st2client/shell.py @@ -50,7 +50,10 @@ from st2client.config import set_config from st2client.exceptions.operations import OperationFailureException from st2client.utils.logging import LogLevelFilter, set_log_level_for_all_loggers -from st2client.commands.auth import TokenCreateCommand, LoginCommand +from st2client.commands.auth import TokenCreateCommand +from st2client.commands.auth import LoginCommand +from st2client.commands.auth import WhoamiCommand + __all__ = [ 'Shell' @@ -66,7 +69,8 @@ class Shell(BaseCLIApp): SKIP_AUTH_CLASSES = [ TokenCreateCommand.__name__, - LoginCommand.__name__ + LoginCommand.__name__, + WhoamiCommand.__name__ ] def __init__(self): diff --git a/st2client/tests/base.py b/st2client/tests/base.py index 49d2fa6fa8..9f40ff0329 100644 --- a/st2client/tests/base.py +++ b/st2client/tests/base.py @@ -77,6 +77,8 @@ class BaseCLITestCase(unittest2.TestCase): stdout = six.moves.StringIO() stderr = six.moves.StringIO() + DEFAULT_SKIP_CONFIG = '1' + def setUp(self): super(BaseCLITestCase, self).setUp() @@ -86,7 +88,7 @@ def setUp(self): if var in os.environ: del os.environ[var] - os.environ['ST2_CLI_SKIP_CONFIG'] = '1' + os.environ['ST2_CLI_SKIP_CONFIG'] = self.DEFAULT_SKIP_CONFIG if self.capture_output: # Make sure we reset it for each test class instance diff --git a/st2client/tests/unit/test_auth.py b/st2client/tests/unit/test_auth.py index 1e4c72c664..7135e31fd1 100644 --- a/st2client/tests/unit/test_auth.py +++ b/st2client/tests/unit/test_auth.py @@ -61,6 +61,9 @@ class TestLoginBase(base.BaseCLITestCase): def __init__(self, *args, **kwargs): super(TestLoginBase, self).__init__(*args, **kwargs) + # We're overriding the default behavior for CLI test cases here + self.DEFAULT_SKIP_CONFIG = '0' + self.parser = argparse.ArgumentParser() self.parser.add_argument('-t', '--token', dest='token') self.parser.add_argument('--api-key', dest='api_key') @@ -142,6 +145,9 @@ class TestLoginIntPwdAndConfig(TestLoginBase): @mock.patch.object( requests, 'post', mock.MagicMock(return_value=base.FakeResponse(json.dumps(TOKEN), 200, 'OK'))) + @mock.patch.object( + requests, 'get', + mock.MagicMock(return_value=base.FakeResponse(json.dumps({}), 200, 'OK'))) @mock.patch('st2client.commands.auth.getpass') def runTest(self, mock_gp): '''Test 'st2 login' functionality with interactive password entry @@ -154,10 +160,14 @@ def runTest(self, mock_gp): self.shell.run(args) - with open(self.CONFIG_FILE, 'r') as config_file: + expected_kwargs = { + 'headers': {'content-type': 'application/json'}, + 'auth': ('st2admin', 'Password1!') + } + requests.post.assert_called_with('http://127.0.0.1:9100/tokens', '{}', **expected_kwargs) + with open(self.CONFIG_FILE, 'r') as config_file: for line in config_file.readlines(): - # Make sure certain values are not present self.assertFalse('password' in line) self.assertFalse('olduser' in line) @@ -169,6 +179,18 @@ def runTest(self, mock_gp): # validate token was created self.assertTrue(os.path.isfile('%stoken-%s' % (self.DOTST2_PATH, expected_username))) + # Validate token is sent on subsequent requests to st2 API + args = ['--config', self.CONFIG_FILE, 'pack', 'list'] + self.shell.run(args) + + expected_kwargs = { + 'headers': { + 'X-Auth-Token': self.TOKEN['token'] + }, + 'params': {} + } + requests.get.assert_called_with('http://127.0.0.1:9101/v1/packs', **expected_kwargs) + class TestLoginWritePwdOkay(TestLoginBase):