snowflakedb / snowflake-cli

Snowflake CLI is an open-source command-line tool explicitly designed for developer-centric workloads in addition to SQL operations.
https://docs.snowflake.com/developer-guide/snowflake-cli-v2/index
Apache License 2.0
178 stars 56 forks source link

SNOW-1752110: generate-jwt should not fail on empty passphrase #1747

Open sfc-gh-vtimofeenko opened 1 week ago

sfc-gh-vtimofeenko commented 1 week ago

SnowCLI version

Snowflake CLI version: 3.2.0.dev0

Python version

Python 3.11.6

Platform

macOS-14.7-arm64-arm-64bit

What happened

For a key without passphrase, the passphrase is passed as empty string instead of None.

Potential fixes

Use dynamic defaults for prompts

Move passphrase to arguments for generate_jwt function and use this click approach allows automating the "don't show prompt if envvar is set".

Use value_proc in typer's prompt

Basically this will use a tiny value post-processor which turns empty string into None. Seems to work (all tests pass, jwt is generated); I'd be happy to submit a PR.

diff --git a/src/snowflake/cli/_plugins/connection/commands.py b/src/snowflake/cli/_plugins/connection/commands.py
index 543f70f2..0b34eec2 100644
--- a/src/snowflake/cli/_plugins/connection/commands.py
+++ b/src/snowflake/cli/_plugins/connection/commands.py
@@ -13,6 +13,7 @@
 # limitations under the License.

 from __future__ import annotations
+from typing import Optional

 import logging
 import os.path
@@ -386,12 +387,18 @@ def generate_jwt(
 ) -> CommandResult:
     """Generate a JWT token, which will be printed out and displayed.."""
     passphrase = os.getenv("PRIVATE_KEY_PASSPHRASE", None)
+
+    def maybe_str(x: str) -> Optional[str]:
+        """Parser turns empty string into None."""
+        return None if x == "" else str(x)
+
     if not passphrase:
         passphrase = typer.prompt(
             "Enter private key file password (Press enter if none)",
             hide_input=True,
             type=str,
             default="",
+            value_proc=maybe_str,
         )
     try:
         token = connector.auth.get_token_from_private_key(

Console output

❯ snow connection generate-jwt --private-key <path-to-p8>                         
Enter private key file password (Press enter if none) []: # Press enter here

An unexpected exception occurred. Use --debug option to see the traceback. Exception message:

Password was given but private key is not encrypted.

How to reproduce

  1. Set up a Snowflake user with key-pair auth, don't set the passphrase
  2. Try to generate jwt for the user
sfc-gh-turbaszek commented 1 week ago

Interesting, I can't replicate the behavior. After this change I'm getting TypeError: Password was not given but private key is encrypted on my key. While it works fine without your change. FYI the implementation is based on snowsql --generate-jwt

sfc-gh-vtimofeenko commented 1 week ago

Looks like the latest version produces the same error without my patch:

❯ pipx install git+https://github.com/snowflakedb/snowflake-cli.git           
⚠️  Note: snow was already on your PATH at /Users/vtimofeenko/.nix-profile/bin/snow
  installed package snowflake-cli 3.2.0.dev0, installed using Python 3.11.9
  These apps are now globally available
    - snow
done! ✨ 🌟 ✨
❯ ~/.local/bin/snow --version
Snowflake CLI version: 3.2.0.dev0
❯ ~/.local/bin/snow --config-file=./test.toml connection generate-jwt
...config_manager.py:351: UserWarning: Bad owner or permissions on test.toml.
 * To change owner, run `chown $USER "test.toml"`.
 * To restrict permissions, run `chmod 0600 "test.toml"`.

  warn(f"Bad owner or permissions on {str(filep)}{chmod_message}")
Enter private key file password (Press enter if none) []: 

An unexpected exception occurred. Use --debug option to see the traceback. Exception message:

Password was given but private key is not encrypted.
❯ cat test.toml                                                
[connections.default]
account = "<REDACTED>"
user = "snowcli_key_test"
private_key_path = "<REDACTED>"

If using RC4:

❯ pipx install git+https://github.com/snowflakedb/snowflake-cli.git@v3.1.0-rc4
⚠️  Note: snow was already on your PATH at /Users/vtimofeenko/.nix-profile/bin/snow
  installed package snowflake-cli 3.1.0rc4, installed using Python 3.11.9
  These apps are now globally available
    - snow
done! ✨ 🌟 ✨
❯ ~/.local/bin/snow --version                                        
Snowflake CLI version: 3.1.0rc4
❯ ~/.local/bin/snow --config-file=./test.toml connection generate-jwt
# <same complaint about permissions>
Enter private key file password (Press enter if none) []: 

An unexpected exception occurred. Use --debug option to see the traceback. Exception message:

Password was given but private key is not encrypted.