Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support other nextstrain.org-like remotes #333

Merged
merged 4 commits into from
Jan 18, 2024
Merged

Support other nextstrain.org-like remotes #333

merged 4 commits into from
Jan 18, 2024

Conversation

tsibley
Copy link
Member

@tsibley tsibley commented Nov 22, 2023

Adds support for other nextstrain.org-like remotes to the nextstrain remote family of commands, along with support for OIDC/OAuth2 authentication with them to the nextstrain login and related commands. Required IdP and client configuration for OIDC/OAuth2 is auto-discovered.

One giant pile of changes, because I did not have time to go thru my typical editing and splitting process before going on leave. :/ Ah well.

Related-to: https://github.com/nextstrain/private/issues/94


Builds upon #332.

@victorlin I'm hopeful you can pick this up if needed before I'm back. I wish I'd had time to break it up into smaller changes.

At the least, without any additional work, this should provide CI builds of the standalone archives for testing, e.g. by installing them with:

curl -fsSL --proto '=https' https://nextstrain.org/cli/installer/linux | bash -s pr-build/333

To test this against our Azure AD/Entra ID testing directory, I created an app registration for the CLI (68e51465-2672-4c47-92ab-bec0e919cf7a). This required mapping AD/security groups to app roles, just like was done for the web server client. I then ran the nextstrain.org codebase with this bit of config.

{
  "AWS_REGION": "us-east-1",
  "OIDC_IDP_URL": "https://login.microsoftonline.com/0ce9e8dc-e009-4cb4-8512-7989bd6906a8/v2.0",
  "OAUTH2_CLIENT_ID": "c3d6647f-dccc-4a85-a2bb-fb8fbc7524a9",
  "OAUTH2_CLI_CLIENT_ID": "68e51465-2672-4c47-92ab-bec0e919cf7a",
  "OAUTH2_CLI_CLIENT_REDIRECT_URIS": ["http://127.0.0.1"],
  "OIDC_USERNAME_CLAIM": "preferred_username",
  "OIDC_GROUPS_CLAIM": "roles",
  "OIDC_IAT_BACKDATED_BY": 300,
  "GROUPS_DATA_FILE": "groups.json"
}

Note the redirect URI! This is the appropriate URI for production too, not just testing. A secret is also required, which is configured by env var.

Testing looks like then:

nextstrain login localhost:5000  # the local nextstrain.org server, as configured above
nextstrain whoami localhost:5000
nextstrain remote ls localhost:5000/groups/test-private/

Checklist

  • Checks pass
  • Type checking failures need to be sorted out
  • Further testing internally and by CDC
  • Later: document remotes and authn flow (Remotes doc #334)

Copy link
Member

@victorlin victorlin left a comment

Choose a reason for hiding this comment

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

Forgot to mention that I made a few patches while looking into this for usage by CDC. Consider them as suggestions for this PR: 39fdb8a...ea3d185

@tsibley
Copy link
Member Author

tsibley commented Jan 12, 2024

Great, thanks!

@tsibley
Copy link
Member Author

tsibley commented Jan 12, 2024

Brought all three into my local branch with:

git am < <(curl -fsSL https://github.com/nextstrain/cli/compare/39fdb8a44334f6d8fce67a958c534bc7d880a9d2...ea3d1851becf1ab2b6bf1257c2338525414190f9.patch)

and will probably fix them up into the primary commit.

Mypy 1.8.0 and some earlier versions complain about these two imports:

    $ mypy -p nextstrain.cli
    nextstrain/cli/runner/singularity.py:98: error: Name "docker" already defined (by an import)  [no-redef]
    nextstrain/cli/runner/aws_batch/__init__.py:94: error: Name "docker" already defined (by an import)  [no-redef]
    Found 2 errors in 2 files (checked 61 source files)

…but there is no other definition of "docker" in those files!  Mypy's
got something wrong.  These seem like new complaints as the code has
existed for a while.

Pyright does not complain.  Adding reveal_type(docker) before the
imports to see what each thinks produces:

    note: Revealed type is "types.ModuleType"

from Mypy and

    information: Type of "docker" is "Unbound"

from Pyright.
@tsibley tsibley force-pushed the trs/remotes branch 2 times, most recently from 288be61 to e74ee80 Compare January 12, 2024 23:01
Adds support for other nextstrain.org-like remotes to the `nextstrain
remote` family of commands, along with support for OIDC/OAuth2
authentication with them to the `nextstrain login` and related commands.
Required IdP and client configuration for OIDC/OAuth2 is
auto-discovered.

One giant pile of changes, because I did not have time to go thru my
typical editing and splitting process before going on leave. :/ Ah well.

Related-to: <nextstrain/private#94>
It's handy and reassuring to be able to clear all login credentials
without remembering what remotes you're logged into.
@tsibley tsibley marked this pull request as ready for review January 13, 2024 01:16
@tsibley tsibley requested a review from a team January 13, 2024 01:16
@victorlin
Copy link
Member

victorlin commented Jan 17, 2024

Steps to test this for current Nextstrain users:

# Replace 'mac' with 'linux' or 'windows' if necessary
curl -fsSL --proto '=https' https://nextstrain.org/cli/installer/mac | bash -s pr-build/333

# Check you have the expected version
nextstrain version
# 7.4.0+git.aa9795f

nextstrain logout

nextstrain login
# Follow prompts

@huddlej
Copy link
Contributor

huddlej commented Jan 17, 2024

@victorlin Those commands all ran as expected. I was able to remote ls and remote download with a private group after logging in.

@huddlej
Copy link
Contributor

huddlej commented Jan 17, 2024

@tsibley Confirmed that I wasn't testing the right thing because I installed the new CLI version and then ran a different older version that happened to have priority in my PATH. After confirming I was using the correct version, I got the web-based login and authentication worked as expected. I was surprised by the text output on the terminal, however, that said:

URL: (nevermind!)

@corneliusroemer
Copy link
Member

Tested web login in an Ubuntu docker container and it worked with manual copying of URL

Copy link
Member

@jameshadfield jameshadfield left a comment

Choose a reason for hiding this comment

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

Looks good by (brief) inspection. Minor comments. Tested via an otherwise empty docker image using the default nextstrain.org remote.

  • Login flow via OIDC worked well, required manual pasting of URLs into the browser and then back to the CLI.
  • Login flow using --username worked well.

`--username` or `-u`). See `nextstrain login --help` for more information.
([#333](https://github.com/nextstrain/cli/pull/333))

* With the new support for being logged into multiple remotes, `nextstrain
Copy link
Member

Choose a reason for hiding this comment

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

What's the use case for being logged into multiple remotes simultaneously?

Copy link
Member Author

Choose a reason for hiding this comment

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

CDC will be running a Nextstrain Groups Server internally but also has Groups on nextstrain.org they use, so they'll be accessing two remotes: nextstrain.org and nextstrain.biotech.cdc.gov.

And other organizations have also expressed interest in having a Groups server they can use internally while still maintaining public Groups on nextstrain.org.

Copy link
Member Author

Choose a reason for hiding this comment

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

For development, it's also useful to be able to be manipulate authn with localhost:5000 without disrupting your normal nextstrain.org authn status.


nextstrain login
nextstrain whoami
nextstrain remote ls groups/test-private

Most of the times the above is not necessary, however, as you can specify the
local remote explicitly instead of pretending it's nextstrain.org, e.g.:
Copy link
Member

@jameshadfield jameshadfield Jan 18, 2024

Choose a reason for hiding this comment

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

I interpret this section as saying the following are equivalent:

  1. Set default remote via env variable, e.g. NEXTSTRAIN_DOT_ORG=http://localhost:5000 nextstrain login and NEXTSTRAIN_DOT_ORG=http://localhost:5000 nextstrain remote ls groups/test.
  2. Specify remote in command, e.g. nextstrain login localhost:5000, nextstrain remote ls http://localhost:5000/groups/test

And that neither will access https://nextstrain.org. Is this correct?

Copy link
Member

@jameshadfield jameshadfield Jan 18, 2024

Choose a reason for hiding this comment

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

I have only a cursory understanding of how the CLI, nextstrain.org server (perhaps localhost) and cognito are interacting here.

When we login with nextstrain login localhost:5000 with the nextstrain.org server running at localhost:5000, is this remote the authz server as far as the CLI is concerned? And thus everything to do with cognito (user pools etc) has been lifted out of the CLI and into the remote? And thus any tokens the CLI receives are only ever interacting with the provided remote?

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 interpret this section as saying the following are equivalent: […] And that neither will access https://nextstrain.org/. Is this correct?

They're equivalent except for the short code path before NEXTSTRAIN_DOT_ORG is used to potentially override what "nextstrain.org" means (in nextstrain.cli.remote.parse_remote_path()). That is, once the remote is parsed, they're equivalent.

Neither will access https://nextstrain.org itself.

When we login with nextstrain login localhost:5000 with the nextstrain.org server running at localhost:5000, is this remote the authz server as far as the CLI is concerned? And thus everything to do with cognito (user pools etc) has been lifted out of the CLI and into the remote? And thus any tokens the CLI receives are only ever interacting with the provided remote?

No, the CLI still interacts with the OIDC/OAuth2 authorization server (aka the IdP), which in our case is Cognito but in CDC's case is Entra ID (née Azure AD). It obtains the IdP information from the remote, so it doesn't have to be hardcoded. I do have a rough WIP of docs for this interaction to help folks understand, but it's not up yet.

Copy link
Member

Choose a reason for hiding this comment

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

Ok. At least this way the identity provider configuration has been lifted out of the CLI and into the remote.

print(f"Logged into nextstrain.org as {user.username}.")
print("Log out with `nextstrain logout`.")
print(f"Logged into {url.origin} as {user.username}.")
print(f"Log out with `nextstrain logout {shquote(url.origin)}`.")
Copy link
Member

Choose a reason for hiding this comment

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

I would favor not printing the remote when it's the default (nextstrain.org).

Copy link
Member Author

Choose a reason for hiding this comment

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

Hmm, I don't agree. I think because multiple remotes are possible, it's good to always be explicit to avoid confusing output.

@tsibley
Copy link
Member Author

tsibley commented Jan 18, 2024

Overall feedback seems positive and CDC is already running (successfully) the development builds from this PR, so I'll plan to merge and release today.

I'd definitely welcome a deeper review of code and functionality and would be happy to discuss post-merge suggestions.

@tsibley tsibley merged commit cc0036b into master Jan 18, 2024
36 checks passed
@tsibley tsibley deleted the trs/remotes branch January 18, 2024 20:10
Copy link
Member

@victorlin victorlin left a comment

Choose a reason for hiding this comment

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

Lots to digest in b4b29d0 but it was understandable. Looks good 👍

nextstrain/cli/authn/configuration.py Show resolved Hide resolved
doc/commands/whoami.rst Show resolved Hide resolved
nextstrain/cli/authn/__init__.py Show resolved Hide resolved
nextstrain/cli/authn/session.py Show resolved Hide resolved
tsibley added a commit that referenced this pull request Jan 26, 2024
@victorlin pointed out¹ that a UserError is not raised by this function.

While correcting that, I noted that other docstrings in this module use
"saved credentials" as the term (matching the term we use in
user-visible output) instead of "tokens", so I made this docstring
follow suit.

¹ <#333 (comment)>
tsibley added a commit that referenced this pull request Jan 27, 2024
@victorlin suggested¹ we improve error handling in this code path, and
he's right.

Catch and improve the output for the three most common errors we'll
encounter: connection errors, HTTP errors, and JSON decoding errors.
The original error is summarized in the error output, and by chaining
the new exception from the original, the full chain in all its detail
will be printed when NEXTSTRAIN_DEBUG=1 for troubleshooting.

¹ <#333 (comment)>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants