Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
ddb85ff
Initial work on new 'login' command
Dec 15, 2016
7c97cb5
Fixed inappropriate args to auth.LoginCommand, finished functionality
Dec 15, 2016
a8f4f36
Added unit test for login command
Dec 16, 2016
67a18aa
Removed erroneous extra line
Dec 16, 2016
2d31ff1
Removed extra line in test
Dec 16, 2016
03fb3f6
Simplified expanduser() call
Dec 16, 2016
66fd541
Merge branch 'master' of github.com:StackStorm/st2 into st2-login
Dec 16, 2016
eb3aedc
Added -w option, updated unit test
Dec 16, 2016
a050a9f
Properly mocked 'requests' like I should have already done
Dec 16, 2016
683cb4d
Used built-in function to determine config location
Dec 16, 2016
7b9dcb6
Added mock to config file in shell
Dec 17, 2016
47529e0
Writing password field to config file is now totally optional
Dec 17, 2016
9ae4d07
Used existing constants for location of config dir
Dec 20, 2016
dafb4e1
Fixed typo in config, and added unit test to catch it
Dec 20, 2016
2a4d38d
Added parentheses to str concat for clarity
Dec 20, 2016
9ca6a40
Added more detailed message for login failures
Dec 20, 2016
ec480ff
Removed unnecessary setUp and tearDown methods
Dec 20, 2016
05a4987
Cleaned up some unnecesssarily detailed reset_mock calls
Dec 20, 2016
d45b9af
Refactored and split up unit tests for 'st2 login'
Dec 20, 2016
51de077
Merge branch 'master' into st2-login
Feb 3, 2017
7a2ce39
Updated write-password arg to better reflect functionality
Feb 3, 2017
079c89b
Provided more reasonable fix to previous logging issue
Feb 3, 2017
8f68d55
Removed commented out old code
Feb 3, 2017
5f87719
Introduce 'st2 whoami' command and tests
Feb 3, 2017
3f2c565
Updated changelog
Feb 3, 2017
da93e1b
Add logic to remove password field so that previous passwords don't l…
Feb 4, 2017
3da2b42
Changed erroneous hash to star in changelog
Feb 6, 2017
369978e
Merge branch 'master' into st2-login
Feb 7, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ in development
* Let querier plugin decide whether to delete state object on error. Mistral querier will
delete state object on workflow completion or when the workflow or task references no
longer exists. (improvement)
* Added support for `st2 login` and `st2 whoami` commands. These add some additional functionality
beyond the existing `st2 auth` command and actually works with the local configuration so that
users do not have to.

2.1.1 - December 16, 2016
-------------------------
Expand Down
3 changes: 2 additions & 1 deletion st2client/st2client/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

import os
import json
import logging
import time
import calendar
import traceback
Expand Down Expand Up @@ -56,7 +57,7 @@ class BaseCLIApp(object):
Base class for StackStorm CLI apps.
"""

LOG = None # logger instance to use
LOG = logging.getLogger(__name__) # logger instance to use
client = None # st2client instance

# A list of command classes for which automatic authentication should be skipped.
Expand Down
123 changes: 122 additions & 1 deletion st2client/st2client/commands/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,13 @@
# See the License for the specific language governing permissions and
# limitations under the License.

from configparser import ConfigParser
import getpass
import json
import logging

from st2client.base import BaseCLIApp
from st2client import config_parser
from st2client import models
from st2client.commands import resource
from st2client.commands.noop import NoopCommand
Expand All @@ -37,7 +40,7 @@ def __init__(self, resource, *args, **kwargs):

super(TokenCreateCommand, self).__init__(
resource, kwargs.pop('name', 'create'),
'Authenticate user and aquire access token.',
'Authenticate user and acquire access token.',
*args, **kwargs)

self.parser.add_argument('username',
Expand Down Expand Up @@ -70,6 +73,124 @@ def run_and_print(self, args, **kwargs):
attributes=self.display_attributes, json=args.json, yaml=args.yaml)


class LoginCommand(resource.ResourceCommand):
display_attributes = ['user', 'token', 'expiry']

def __init__(self, resource, *args, **kwargs):

kwargs['has_token_opt'] = False

super(LoginCommand, self).__init__(
resource, kwargs.pop('name', 'create'),
'Authenticate user, acquire access token, and update CLI config directory',
*args, **kwargs)

self.parser.add_argument('username',
help='Name of the user to authenticate.')

self.parser.add_argument('-p', '--password', dest='password',
help='Password for the user. If password is not provided, '
'it will be prompted.')
self.parser.add_argument('-l', '--ttl', type=int, dest='ttl', default=None,
help='The life span of the token in seconds. '
'Max TTL configured by the admin supersedes this.')
self.parser.add_argument('-w', '--write-password', action='store_true', default=False,
dest='write_password',
help='Write the password in plain text to the config file '
'(default is to omit it')

def run(self, args, **kwargs):

if not args.password:
args.password = getpass.getpass()
instance = self.resource(ttl=args.ttl) if args.ttl else self.resource()

cli = BaseCLIApp()

# Determine path to config file
try:
config_file = cli._get_config_file_path(args)
except ValueError:
# config file not found in args or in env, defaulting
config_file = config_parser.ST2_CONFIG_PATH

# Retrieve token
manager = self.manager.create(instance, auth=(args.username, args.password), **kwargs)
cli._cache_auth_token(token_obj=manager)

# Update existing configuration with new credentials
config = ConfigParser()
config.read(config_file)

# Modify config (and optionally populate with password)
if 'credentials' not in config:
config.add_section('credentials')
config['credentials'] = {}
config['credentials']['username'] = args.username
if args.write_password:
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

scenario: configure .st2/config with user1 with password, rewriting it with st2 login user2, user get rewriten, password remains from user1, token expires, user1 no longer works.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point - so perhaps I can default to deleting the "password" field (or if --write-password is provided, overwrite)?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dzimine I took a stab at this in da93e1b - let me know what you think. Seems to work well now for this case

config['credentials']['password'] = args.password
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

else:
# Remove any existing password from config
config['credentials'].pop('password', None)

with open(config_file, "w") as cfg_file_out:
config.write(cfg_file_out)

return manager

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)))
if self.app.client.debug:
raise


class WhoamiCommand(resource.ResourceCommand):
Copy link
Member

@Kami Kami Feb 6, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to confirm - does this follow the same logic as other commands - aka it takes ST2_CONFIG_PATH environment variable, --config-file (or whatever the argument name is), --user, etc. and other things into account when determining the username?

If not, it would be a good idea to do that otherwise it might be confusing to the user.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To clarify, that are the options I had in mind - https://docs.stackstorm.com/reference/cli.html#configuration-file.

Copy link
Member Author

@Mierdin Mierdin Feb 6, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Kami Okay, I tested both the ST2_CONFIG_PATHand--config-file` options, and other than running into this bug (unrelated to this patch) everything seems to work using a totally different config path.

It should be noted that this tool doesn't actually create the configuration file - as with other commands, if the file doesn't exist at the referenced path, it will raise an exception. I did this to be as predictable compared to other st2 commands as possible.

I will push a short note to StackStorm/st2docs#380 to be clear on this

display_attributes = ['user', 'token', 'expiry']

def __init__(self, resource, *args, **kwargs):

kwargs['has_token_opt'] = False

super(WhoamiCommand, self).__init__(
resource, kwargs.pop('name', 'create'),
'Display the currently authenticated/configured user',
*args, **kwargs)

def run(self, args, **kwargs):

cli = BaseCLIApp()

# Determine path to config file
try:
config_file = cli._get_config_file_path(args)
except ValueError:
# config file not found in args or in env, defaulting
config_file = config_parser.ST2_CONFIG_PATH

# Update existing configuration with new credentials
config = ConfigParser()
config.read(config_file)

return config['credentials']['username']

def run_and_print(self, args, **kwargs):
try:
username = self.run(args, **kwargs)
print("Currently logged in as %s" % username)
except KeyError:
print("No user is currently logged in")
if self.app.client.debug:
raise
except Exception:
print("Unable to retrieve currently logged-in user")
if self.app.client.debug:
raise


class ApiKeyBranch(resource.ResourceBranch):

def __init__(self, description, app, subparsers, parent_parser=None):
Expand Down
11 changes: 9 additions & 2 deletions st2client/st2client/shell.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
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
from st2client.commands.auth import TokenCreateCommand, LoginCommand

__all__ = [
'Shell'
Expand All @@ -65,7 +65,8 @@ class Shell(BaseCLIApp):
LOG = LOGGER

SKIP_AUTH_CLASSES = [
TokenCreateCommand.__name__
TokenCreateCommand.__name__,
LoginCommand.__name__
]

def __init__(self):
Expand Down Expand Up @@ -188,6 +189,9 @@ def __init__(self):
'for reuse in sensors, actions, and rules.',
self, self.subparsers)

self.commands['login'] = auth.LoginCommand(
models.Token, self, self.subparsers, name='login')

self.commands['pack'] = pack.PackBranch(
'A group of related integration resources: '
'actions, rules, and sensors.',
Expand Down Expand Up @@ -235,6 +239,9 @@ def __init__(self):
'Webhooks.',
self, self.subparsers)

self.commands['whoami'] = auth.WhoamiCommand(
models.Token, self, self.subparsers, name='whoami')

self.commands['timer'] = timer.TimerBranch(
'Timers.',
self, self.subparsers)
Expand Down
Loading