Skip to content

Conversation

@Mierdin
Copy link
Member

@Mierdin Mierdin commented Dec 16, 2016

Overview

This PR introduces a new st2 login command to the StackStorm client. This command is similar to st2 auth, as it does generate a user token, but it also modifies ~/.st2/config and adds a token file to the ~/.st2/config directory. Further st2 commands will use the new username/token combination that was written to disk.

This is in response to #3110, where a user asked if there was a more friendly way of specifying the current user (i.e. without explicitly modifying configuration files or setting environment variables). This command is simply an alternative to those options.

Initially, I considered adding a flag to st2 auth that caches the token in ~/.st2/token-<username> but realized that ~/st2/config would also need to be modified to specify the "current" user. This seemed like a bit too much functionality to have in a single "flag" off of st2 auth, so I decided a separate command was ideal, and more self-explanatory.

By default, this command will write only the username to the configuration, for security purposes (not needed if a token file is present). The user can use the -w option to override this default and write the password into the config file as well.

Note, an st2docs PR will follow this, to document the new command. I just wanted to make sure the approach was valid before I started updating docs.

Example

Here's the new command in action. Note that the configuration file is updated and only the relevant section ("credentials") is written to - the rest is preserved.

(virtualenv)vagrant@st2dev:~/st2$ ll ~/.st2
total 12
drwxrwxr-x 2 vagrant vagrant 4096 Dec 16 17:42 ./
drwxr-xr-x 9 vagrant vagrant 4096 Dec 16 17:42 ../
-rw-rw-r-- 1 vagrant vagrant   40 Dec 16 17:42 config
(virtualenv)vagrant@st2dev:~/st2$ cat ~/.st2/config
[extrasection]
foo1 = bar1
foo2 = bar2

(virtualenv)vagrant@st2dev:~/st2$ st2 login st2admin --password Password1!
Logged in as st2admin
(virtualenv)vagrant@st2dev:~/st2$ cat ~/.st2/config
[extrasection]
foo1 = bar1
foo2 = bar2

[credentials]
username = st2admin

(virtualenv)vagrant@st2dev:~/st2$ cat ~/.st2/token-st2admin
{"expire_timestamp": 1481996584, "token": "a89b4a5fcc0542bcac48d3d9ef67c8c9"}(virtualenv)vagrant@st2dev:~/st2$

On the other hand, a failed login attempt will do nothing:

(virtualenv)vagrant@st2dev:~/st2$ ll ~/.st2/
total 12
drwxrwxr-x 2 vagrant vagrant 4096 Dec 16 17:45 ./
drwxr-xr-x 9 vagrant vagrant 4096 Dec 16 17:45 ../
-rw-rw-r-- 1 vagrant vagrant   40 Dec 16 17:45 config
(virtualenv)vagrant@st2dev:~/st2$ cat ~/.st2/config
[extrasection]
foo1 = bar1
foo2 = bar2

(virtualenv)vagrant@st2dev:~/st2$ st2 login st2admin --password wrongpassword
Failed to log in as st2admin
(virtualenv)vagrant@st2dev:~/st2$ ll ~/.st2/
total 12
drwxrwxr-x 2 vagrant vagrant 4096 Dec 16 17:45 ./
drwxr-xr-x 9 vagrant vagrant 4096 Dec 16 17:45 ../
-rw-rw-r-- 1 vagrant vagrant   40 Dec 16 17:45 config
(virtualenv)vagrant@st2dev:~/st2$ cat ~/.st2/config
[extrasection]
foo1 = bar1
foo2 = bar2

StackStorm documentation has been updated in StackStorm/st2docs#380

UPDATE: Based on some of the review comments, I've added a few tasks for myself:

  • Add st2 whoami command (thanks for idea @Kami)
  • Address logging configuration brought up in Adding support for st2 login and st2 whoami commands #3123 (comment)
  • Update changelog
  • It's been a while since I looked at this PR - after all the above has been done, need to go through a full end-to-end test and ensure that everything shown in the examples above still works, and also verify that it still handles minor issues like ~/.st2/ not existing, etc.
  • Documentation tasks. Make sure the descriptions between login and auth commands are sufficiently distinct. Also at least open a st2docs PR and link here

Closes #3110

Matt Oswalt added 4 commits December 15, 2016 10:58
Signed-off-by: Matt Oswalt <oswaltm@brocade.com>
Signed-off-by: Matt Oswalt <oswaltm@brocade.com>
Signed-off-by: Matt Oswalt <oswaltm@brocade.com>
Signed-off-by: Matt Oswalt <oswaltm@brocade.com>
Copy link
Contributor

@bigmstone bigmstone left a comment

Choose a reason for hiding this comment

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

Looking good. I have a few questions/comments. Also what do you do in the event that the ~/.st2 folder doesn't exist? I'm of the opinion this tool should create it.

cli._cache_auth_token(token_obj=manager)

# Update existing configuration with new credentials
config_file = "%s/.st2/config" % expanduser("~")
Copy link
Contributor

Choose a reason for hiding this comment

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

Should use os.path.join or expanduser('~.st2/config') here.

Copy link
Member Author

Choose a reason for hiding this comment

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

Done (03fb3f6).

# Update existing configuration with new credentials
config_file = "%s/.st2/config" % expanduser("~")
config = ConfigParser()
config.read(config_file)
Copy link
Contributor

@bigmstone bigmstone Dec 16, 2016

Choose a reason for hiding this comment

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

What happens if this fails?

Copy link
Member Author

Choose a reason for hiding this comment

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

No exception is raised, just an empty list is returned.

(Pdb) config.read("/tmp/foodoesntexist.txt")
[]

This just means there wasn't a file there to populate the config object.

If I run this with the real code, and the config file already exists, you see this:

(Pdb) config.read(config_file)
['/home/vagrant/.st2/config']

Either way, we're always overwriting the two parameters in the "credentials" section, and writing it to disk.

"username": args.username,
"password": args.password
}
with open(config_file, "w") as cfg_file_out:
Copy link
Contributor

Choose a reason for hiding this comment

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

Should be w+ to create file if it doesn't exist.

Copy link
Member Author

Choose a reason for hiding this comment

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

My understanding is that w will still create the file; w+ is used if you want to also read from the file, which in this case I don't care about.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yep, my mistake.

Matt Oswalt added 2 commits December 15, 2016 22:35
Signed-off-by: Matt Oswalt <oswaltm@brocade.com>
Signed-off-by: Matt Oswalt <oswaltm@brocade.com>
@Mierdin
Copy link
Member Author

Mierdin commented Dec 16, 2016

@bigmstone:

Also what do you do in the event that the ~/.st2 folder doesn't exist? I'm of the opinion this tool should create it.

The _cache_auth_token method already has lots of checks like this, and I am doing that first, so if that has an error, the rest of the code won't run; if it doesn't, everything should exist.

You think this still warrants more checks within this function, or is that sufficient?

@codecov-io
Copy link

codecov-io commented Dec 16, 2016

Codecov Report

Merging #3123 into master will not impact coverage by -<.01%.

@@            Coverage Diff             @@
##           master    #3123      +/-   ##
==========================================
- Coverage   77.65%   77.65%   -<.01%     
==========================================
  Files         433      432       -1     
  Lines       22323    22390      +67     
==========================================
+ Hits        17334    17385      +51     
- Misses       4989     5005      +16
Impacted Files Coverage Δ
st2client/st2client/base.py 72.22% <100%> (+10.29%)
st2client/st2client/shell.py 86.99% <100%> (+1.87%)
st2client/st2client/commands/auth.py 68.13% <82.61%> (+8.84%)
...ctions/st2actions/resultstracker/resultstracker.py 72.22% <ø> (-15.28%)
st2common/st2common/query/base.py 70.23% <ø> (-10.97%)
st2exporter/st2exporter/worker.py 58.82% <ø> (-4.71%)
st2common/st2common/services/triggerwatcher.py 87.32% <ø> (-2.82%)
...common/st2common/validators/workflow/mistral/v2.py 97.96% <ø> (-2.04%)
st2common/st2common/bootstrap/aliasesregistrar.py 63% <ø> (-2%)
st2api/st2api/controllers/v1/packs.py 90.12% <ø> (-1.85%)
... and 18 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 200efeb...369978e. Read the comment docs.

@bigmstone
Copy link
Contributor

@Mierdin Looked over _cache_auth_token. 👍

Copy link
Contributor

@bigmstone bigmstone left a comment

Choose a reason for hiding this comment

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

Once you get the linting stuff fixed then you should be 👍. Might want to get someone else to offer their +1 on direction here, too. I personally like the st2 login UX but others might have a different opinion.

Matt Oswalt added 2 commits December 16, 2016 10:10
Signed-off-by: Matt Oswalt <oswaltm@brocade.com>
Signed-off-by: Matt Oswalt <oswaltm@brocade.com>
@Mierdin
Copy link
Member Author

Mierdin commented Dec 16, 2016

@bigmstone wanna take another quick look? Lots of changes in eb3aedc so maybe you might want to take a peek again?

cli._cache_auth_token(token_obj=manager)

# Update existing configuration with new credentials
config_file = expanduser('~/.st2/config')
Copy link
Member

Choose a reason for hiding this comment

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

There is probably constant somewhere to store the ~/.st2/config default value.

Additionally, here might be edge cases:
what if env ST2_CONFIG_FILE or --config-file= flag is provided? See the docs: https://docs.stackstorm.com/reference/cli.html?highlight=cli%20authentication#configuration-file

I think we should support that for consistency.

Copy link
Member Author

Choose a reason for hiding this comment

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

This is a good idea, and I used the function _get_config_file_path in BaseCLI to ensure this consistency. Implemented in 683cb4d

This is how it works now (note if the --config option is omitted, that still works and writes to the default location:

(virtualenv)vagrant@st2dev:~/st2/st2client$ st2 --config /tmp/st2config login st2admin --password Password1!
Logged in as st2admin
(virtualenv)vagrant@st2dev:~/st2/st2client$ cat /tmp/st2config
[credentials]
username = st2admin
password = notarealpassword

# option is provided, in which case we'll write the real password.
config['credentials'] = {
"username": args.username,
"password": args.password if args.write_password else "notarealpassword"
Copy link
Member

Choose a reason for hiding this comment

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

Writing notarealpassword to conf file looks really really strange

Copy link
Member Author

Choose a reason for hiding this comment

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

What else should I write? I originally tried omitting the field altogether, but it seems that other st2 commands actually require this field, yet don't use it. Writing an obviously fake password to this field seemed like the simplest approach.

}

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

@arm4b arm4b Dec 16, 2016

Choose a reason for hiding this comment

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

What if correct credentials already exist in the file. Do we overwrite them with "fake password"?
If so, looks like "regression", no?

Copy link
Member Author

Choose a reason for hiding this comment

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

I don't think so, the whole point of st2 login is to change this configuration, so the user should expect it to get overwritten with whatever they put into the args or getpass

@arm4b
Copy link
Member

arm4b commented Dec 16, 2016

If ~/.st2 directory doesn't exist, will it be created by st2 login?

We create that dir via curl | bash script, but it's not single point of truth.
People can install StackStorm with configuration management or manually via apt-get/yum install.

For me, st2 login -w could be useful as a way to run it once after clean st2 installation to store the password. See example: StackStorm/chef-stackstorm#41

Additionally, we could replace this bashery: https://github.com/StackStorm/st2-packages/blob/master/scripts/st2bootstrap-deb.sh#L219-L259 with new functionality provided in this PR.

@Mierdin
Copy link
Member Author

Mierdin commented Dec 16, 2016

@armab See #3123 (comment) - the method that I'm calling to generate a new token does all kinds of useful checks to make sure the directory is created if it doesn't exist. I am making sure that call is done first, so I know what files/directories will be on the system at that point.

@m4dcoder
Copy link
Contributor

I'm going to need more clarification from the team here. I looked at the original request. Wouldn't writing/caching the token be sufficient to meet the requirement? How come we are also rewriting the client config and saving the client credential in the config file. That seems insecure and not obvious to the user that their credential will be written to disk.

@Mierdin
Copy link
Member Author

Mierdin commented Dec 16, 2016

@m4dcoder the reason I'm modifying the config is all about instructing other st2 commands which token file to use, via the username. Tokens are cached by username (i.e. ~/.st2/token-st2admin), and my understanding is that the client uses the username in the configuration to determine which token file to reference (helpful when there are multiple).

So, st2 login modifies the configuration by writing the username specified via args. By doing this, other st2 commands know which token file to read to get the token.

Note also by default it writes a dummy/fake password to the config file. Other st2 commands apparently require that this field is present in the config (I tested without), but the field is not used if the token file is found, so the password doesn't need to be correct.

The user request was all about being able to quick switch between users, and this accomplishes this in the most secure way I know how, and doesn't rely on being able to modify environment variables in the calling shell (which you can't do in Python anyways). The config file in this case just acts as a pointer to which token file to use at a given time.

Hope that helps clear things up

@m4dcoder
Copy link
Contributor

Can you figure out why the password is needed (even a fake password) in the config file? I prefer we fix this and don't write a fake password. It's just confusing code wise and users who is looking at the config will question that anyway.

This allows the user to specify a different location for the config file using the
already-existing "--config" argument

Signed-off-by: Matt Oswalt <oswaltm@brocade.com>
@Mierdin
Copy link
Member Author

Mierdin commented Dec 16, 2016

I agree - will hunt that down next.

@Mierdin
Copy link
Member Author

Mierdin commented Dec 16, 2016

@m4dcoder It seems I was mistaken about the password field. I must have lost track of my steps during initial testing; I can perform queries without the password field in the config:

(virtualenv)vagrant@st2dev:~/st2/st2client$ cat ~/.st2/config
[extrasection]
foo1 = bar1
foo2 = bar2

[credentials]
username = st2admin

(virtualenv)vagrant@st2dev:~/st2/st2client$ cat ~/.st2/token-st2admin
{"expire_timestamp": 1482014621, "token": "ee46b13293b345c4a9f7bcd955854b3e"}
(virtualenv)vagrant@st2dev:~/st2/st2client$ st2 execution list
+----+------------+--------------+--------+-----------------+---------------+
| id | action.ref | context.user | status | start_timestamp | end_timestamp |
+----+------------+--------------+--------+-----------------+---------------+
+----+------------+--------------+--------+-----------------+---------------+

In the next few minutes I'll push another commit that removes the password functionality from this patch.

Matt Oswalt added 2 commits December 16, 2016 16:02
Signed-off-by: Matt Oswalt <oswaltm@brocade.com>
Signed-off-by: Matt Oswalt <oswaltm@brocade.com>
@Mierdin
Copy link
Member Author

Mierdin commented Dec 17, 2016

@armab @m4dcoder I have made the requested changes, can I get another review at your convenience? Thanks and have a great weekend!

config_file = cli._get_config_file_path(args)
except ValueError:
# config file not found in args or in env, defaulting
config_file = expanduser('~/.st2/config')
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you use os.path.expanduser instead and import os above? Also, can you use the constants at https://github.com/StackStorm/st2/blob/master/st2client/st2client/config_parser.py#L36 instead of duplicating and hard coding the path value here?

Copy link
Member Author

Choose a reason for hiding this comment

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

Can do

Copy link
Member Author

@Mierdin Mierdin Dec 20, 2016

Choose a reason for hiding this comment

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

Fixed in: 9ae4d07

"args": ['--config', '/tmp/st2config', 'login', 'st2admin', '-w'],
"expected_username": 'st2admin'
}
]
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you separate into different test methods especially that you've additional things to test for each test case below?

Copy link
Member Author

Choose a reason for hiding this comment

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

I can split it up; the reason I did it this way was to save a little bit of line count considering that the tests cases have a lot in common. Would result in a lot of redundant test code if I split it up

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah, i rather you duplicate here to keep the tests clear. And you can always refactor into private methods for obvious reduction.

Copy link
Member Author

Choose a reason for hiding this comment

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

Okay I split up these tests and added a function _login_common_assertions to save a little bit of linecount on common things between the unit tests. Committed in d45b9af

config['credentials'] = {}
config['credentials']['username'] = args.username
if args.write_password:
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.

👍

Copy link
Member

@arm4b arm4b left a comment

Choose a reason for hiding this comment

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

Overall logic looks much more expected and cleaner to me 👍

@Kami and @m4dcoder might have better idea about the core internals.

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-real-password', action='store_true', default=False,
Copy link
Member

@arm4b arm4b Dec 21, 2016

Choose a reason for hiding this comment

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

Since we removed "fake" password logic, "--write_real_password" naming looks a bit strange now.

Maybe it could be better to rename this flag to --save-password or --write-password or any other analog?

Copy link
Member

@arm4b arm4b Dec 21, 2016

Choose a reason for hiding this comment

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

Additionally, don't forget to update Pull Request description (first message) with new syntax/logic, because we don't save fake password: notarealpassword anymore

Copy link
Member Author

Choose a reason for hiding this comment

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

Updated the description, and changed the argument in 7a2ce39

Matt Oswalt added 5 commits February 3, 2017 11:08
Signed-off-by: Matt Oswalt <oswaltm@brocade.com>
Signed-off-by: Matt Oswalt <oswaltm@brocade.com>
Signed-off-by: Matt Oswalt <oswaltm@brocade.com>
@Mierdin
Copy link
Member Author

Mierdin commented Feb 3, 2017

@Kami Okay - introduced the st2 whoami command in 5f87719, would love to hear your thoughts on the implementation

@Mierdin Mierdin added RFR and removed WIP labels Feb 4, 2017
@Mierdin Mierdin changed the title [WIP] Adding support for st2 login and st2 whoami commands Adding support for st2 login and st2 whoami commands Feb 4, 2017
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

…inger

Signed-off-by: Matt Oswalt <oswaltm@brocade.com>
CHANGELOG.rst Outdated
put the workflow in a PAUSED state in mistral. (improvement)
* Add support for evaluating jinja expressions in mistral workflow definition where yaql
expressions are typically accepted. (improvement)
# Added support for `st2 login` and `st2 whoami` commands. These add some additional functionality
Copy link
Member

Choose a reason for hiding this comment

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

Minor thing - s/#/*/

Copy link
Member Author

Choose a reason for hiding this comment

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

Doh! Fixed in 3da2b42

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

@Kami
Copy link
Member

Kami commented Feb 6, 2017

Overall, nice work and LGTM 👍

Only "blocker" for me right now which needs some clarification is https://github.com/StackStorm/st2/pull/3123/files#r99538549

Besides that, LGTM.

Signed-off-by: Matt Oswalt <oswaltm@brocade.com>
Copy link
Member

@Kami Kami left a comment

Choose a reason for hiding this comment

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

LGTM.

(re-ran the build, looks like a temporary upstream issue)

@Mierdin Mierdin merged commit 8f8ef7e into StackStorm:master Feb 7, 2017
@Mierdin Mierdin deleted the st2-login branch February 7, 2017 18:08
@cognifloyd cognifloyd removed the RFR label Aug 9, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Update CLI authentication to store login

8 participants