From 858e9bec8f841a7e09e79e47b1b072736c0eb982 Mon Sep 17 00:00:00 2001 From: Justin Date: Tue, 22 Oct 2019 00:18:54 -0500 Subject: [PATCH 1/4] Added header option for encode --- jwt/__main__.py | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/jwt/__main__.py b/jwt/__main__.py index bf50aabf..9a6dc893 100644 --- a/jwt/__main__.py +++ b/jwt/__main__.py @@ -43,10 +43,19 @@ def encode_payload(args): payload[k] = v + # Build header object to encode + header = {} + + try: + header = json.loads(args.header) + except Exception as e: + raise ValueError('Error loading header: %s. See --help for usage.' % e) + token = encode( payload, key=args.key, - algorithm=args.algorithm + algorithm=args.algorithm, + headers=header ) return token.decode('utf-8') @@ -90,6 +99,12 @@ def build_argparser(): %(prog)s --key=secret encode foo=bar exp=+10 The exp key is special and can take an offset to current Unix time. + + %(prog)s --key=secret --header='{"typ":"jwt", "alg":"RS256"}' encode is=me + + The header option can be provided for input to encode in the jwt. The format + requires the header be enclosed in single quote and key/value pairs with double + quotes. ''' arg_parser = argparse.ArgumentParser( @@ -119,6 +134,14 @@ def build_argparser(): help='set crypto algorithm to sign with. default=HS256' ) + arg_parser.add_argument( + '--header', + dest='header', + metavar='HEADER', + default=None, + help='set jwt header' + ) + subparsers = arg_parser.add_subparsers( title='PyJWT subcommands', description='valid subcommands', From a7a9d1698bb05665569c31a4ad53acdebcc8732c Mon Sep 17 00:00:00 2001 From: Justin Date: Thu, 24 Oct 2019 23:35:34 -0500 Subject: [PATCH 2/4] added tests and None check --- jwt/__main__.py | 10 +++++----- tests/test_cli.py | 23 ++++++++++++++++++----- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/jwt/__main__.py b/jwt/__main__.py index 9a6dc893..3df79ab7 100644 --- a/jwt/__main__.py +++ b/jwt/__main__.py @@ -45,11 +45,11 @@ def encode_payload(args): # Build header object to encode header = {} - - try: - header = json.loads(args.header) - except Exception as e: - raise ValueError('Error loading header: %s. See --help for usage.' % e) + if args.header: + try: + header = json.loads(args.header) + except Exception as e: + raise ValueError('Error loading header: %s. See --help for usage.' % e) token = encode( payload, diff --git a/tests/test_cli.py b/tests/test_cli.py index a89597d3..e953805a 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -29,6 +29,17 @@ def test_encode_payload_raises_value_error_key_is_required(self): assert 'Key is required when encoding' in str(excinfo.value) + def test_encode_header_raises_value_error_bad_dict(self): + encode_args = ['--key=secret', '--header=dfsfd', 'encode', 'name=Vader', 'job=Sith'] + parser = build_argparser() + + args = parser.parse_args(encode_args) + + with pytest.raises(ValueError) as excinfo: + encode_payload(args) + + assert 'Error loading header:' in str(excinfo.value) + def test_decode_payload_raises_decoded_error(self): decode_args = ['--key', '1234', 'decode', 'wrong-token'] parser = build_argparser() @@ -60,6 +71,7 @@ def patched_sys_stdin_read(): def test_decode_payload_terminal_tty(self, monkeypatch): encode_args = [ '--key=secret-key', + '--header={"alg":"HS256"}', 'encode', 'name=hello-world', ] @@ -88,14 +100,15 @@ def test_decode_payload_raises_terminal_not_a_tty(self, monkeypatch): assert 'Cannot read from stdin: terminal not a TTY' \ in str(excinfo.value) - @pytest.mark.parametrize('key,name,job,exp,verify', [ - ('1234', 'Vader', 'Sith', None, None), - ('4567', 'Anakin', 'Jedi', '+1', None), - ('4321', 'Padme', 'Queen', '4070926800', 'true'), + @pytest.mark.parametrize('key,header,name,job,exp,verify', [ + ('1234', '{}', 'Vader', 'Sith', None, None), + ('4567', '{"typ":"test"}', 'Anakin', 'Jedi', '+1', None), + ('4321', '', 'Padme', 'Queen', '4070926800', 'true'), ]) - def test_encode_decode(self, key, name, job, exp, verify): + def test_encode_decode(self, key, header, name, job, exp, verify): encode_args = [ '--key={0}'.format(key), + '--header={0}'.format(header), 'encode', 'name={0}'.format(name), 'job={0}'.format(job), From dd24df7869f6ee2e4ff897756a2c1323099c6b55 Mon Sep 17 00:00:00 2001 From: Justin Date: Thu, 24 Oct 2019 23:47:14 -0500 Subject: [PATCH 3/4] updated quotes for consistency --- tests/test_cli.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_cli.py b/tests/test_cli.py index 0b3e7390..aff741ab 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -28,7 +28,7 @@ def test_encode_payload_raises_value_error_key_is_required(self): assert "Key is required when encoding" in str(excinfo.value) def test_encode_header_raises_value_error_bad_dict(self): - encode_args = ['--key=secret', '--header=dfsfd', 'encode', 'name=Vader', 'job=Sith'] + encode_args = ["--key=secret", "--header=dfsfd", "encode", "name=Vader", "job=Sith"] parser = build_argparser() args = parser.parse_args(encode_args) @@ -36,7 +36,7 @@ def test_encode_header_raises_value_error_bad_dict(self): with pytest.raises(ValueError) as excinfo: encode_payload(args) - assert 'Error loading header:' in str(excinfo.value) + assert "Error loading header:" in str(excinfo.value) def test_decode_payload_raises_decoded_error(self): decode_args = ["--key", "1234", "decode", "wrong-token"] From ecc874d16cac98aa989e1bfd899d60523574afde Mon Sep 17 00:00:00 2001 From: Justin Date: Thu, 24 Oct 2019 23:52:41 -0500 Subject: [PATCH 4/4] format fixes after tox --- jwt/__main__.py | 10 +++++++--- tests/test_cli.py | 17 ++++++++++++++--- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/jwt/__main__.py b/jwt/__main__.py index 57944711..7d3caca4 100644 --- a/jwt/__main__.py +++ b/jwt/__main__.py @@ -51,9 +51,13 @@ def encode_payload(args): try: header = json.loads(args.header) except Exception as e: - raise ValueError("Error loading header: %s. See --help for usage." % e) + raise ValueError( + "Error loading header: %s. See --help for usage." % e + ) - token = encode(payload, key=args.key, algorithm=args.algorithm, headers=header) + token = encode( + payload, key=args.key, algorithm=args.algorithm, headers=header + ) return token.decode("utf-8") @@ -131,7 +135,7 @@ def build_argparser(): dest="header", metavar="HEADER", default=None, - help="set jwt header" + help="set jwt header", ) subparsers = arg_parser.add_subparsers( diff --git a/tests/test_cli.py b/tests/test_cli.py index aff741ab..ba652808 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -28,7 +28,13 @@ def test_encode_payload_raises_value_error_key_is_required(self): assert "Key is required when encoding" in str(excinfo.value) def test_encode_header_raises_value_error_bad_dict(self): - encode_args = ["--key=secret", "--header=dfsfd", "encode", "name=Vader", "job=Sith"] + encode_args = [ + "--key=secret", + "--header=dfsfd", + "encode", + "name=Vader", + "job=Sith", + ] parser = build_argparser() args = parser.parse_args(encode_args) @@ -67,7 +73,12 @@ def patched_sys_stdin_read(): assert "There was an error decoding the token" in str(excinfo.value) def test_decode_payload_terminal_tty(self, monkeypatch): - encode_args = ["--key=secret-key", "--header={\"alg\":\"HS256\"}", "encode", "name=hello-world"] + encode_args = [ + "--key=secret-key", + '--header={"alg":"HS256"}', + "encode", + "name=hello-world", + ] parser = build_argparser() parsed_encode_args = parser.parse_args(encode_args) token = encode_payload(parsed_encode_args) @@ -98,7 +109,7 @@ def test_decode_payload_raises_terminal_not_a_tty(self, monkeypatch): "key,header,name,job,exp,verify", [ ("1234", "{}", "Vader", "Sith", None, None), - ("4567", "{\"typ\":\"test\"}", "Anakin", "Jedi", "+1", None), + ("4567", '{"typ":"test"}', "Anakin", "Jedi", "+1", None), ("4321", "", "Padme", "Queen", "4070926800", "true"), ], )