Change optparse for argparse. (#238)
This commit is contained in:
parent
1f1d185727
commit
328b3d8566
|
@ -4,4 +4,4 @@ omit =
|
|||
.tox/*
|
||||
setup.py
|
||||
*.egg/*
|
||||
*/__main__.py
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ env:
|
|||
- TOXENV=py35-contrib_crypto
|
||||
- TOXENV=py36-contrib_crypto
|
||||
- TOXENV=py27-contrib_crypto
|
||||
|
||||
install:
|
||||
- pip install -U pip
|
||||
- pip install -U tox coveralls
|
||||
|
|
208
jwt/__main__.py
208
jwt/__main__.py
|
@ -2,48 +2,105 @@
|
|||
|
||||
from __future__ import absolute_import, print_function
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import optparse
|
||||
import sys
|
||||
import time
|
||||
|
||||
from . import DecodeError, __package__, __version__, decode, encode
|
||||
from . import DecodeError, __version__, decode, encode
|
||||
|
||||
|
||||
def main():
|
||||
def encode_payload(args):
|
||||
# Try to encode
|
||||
if args.key is None:
|
||||
raise ValueError('Key is required when encoding. See --help for usage.')
|
||||
|
||||
usage = '''Encodes or decodes JSON Web Tokens based on input.
|
||||
# Build payload object to encode
|
||||
payload = {}
|
||||
|
||||
%prog [options] input
|
||||
for arg in args.payload:
|
||||
k, v = arg.split('=', 1)
|
||||
|
||||
Decoding examples:
|
||||
# exp +offset special case?
|
||||
if k == 'exp' and v[0] == '+' and len(v) > 1:
|
||||
v = str(int(time.time()+int(v[1:])))
|
||||
|
||||
%prog --key=secret json.web.token
|
||||
%prog --no-verify json.web.token
|
||||
# Cast to integer?
|
||||
if v.isdigit():
|
||||
v = int(v)
|
||||
else:
|
||||
# Cast to float?
|
||||
try:
|
||||
v = float(v)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
Encoding requires the key option and takes space separated key/value pairs
|
||||
separated by equals (=) as input. Examples:
|
||||
# Cast to true, false, or null?
|
||||
constants = {'true': True, 'false': False, 'null': None}
|
||||
|
||||
%prog --key=secret iss=me exp=1302049071
|
||||
%prog --key=secret foo=bar exp=+10
|
||||
if v in constants:
|
||||
v = constants[v]
|
||||
|
||||
The exp key is special and can take an offset to current Unix time.\
|
||||
'''
|
||||
p = optparse.OptionParser(
|
||||
usage=usage,
|
||||
payload[k] = v
|
||||
|
||||
token = encode(
|
||||
payload,
|
||||
key=args.key,
|
||||
algorithm=args.algorithm
|
||||
)
|
||||
|
||||
return token.decode('utf-8')
|
||||
|
||||
|
||||
def decode_payload(args):
|
||||
try:
|
||||
if sys.stdin.isatty():
|
||||
token = sys.stdin.read()
|
||||
else:
|
||||
token = args.token
|
||||
|
||||
token = token.encode('utf-8')
|
||||
data = decode(token, key=args.key, verify=args.verify)
|
||||
|
||||
return json.dumps(data)
|
||||
|
||||
except DecodeError as e:
|
||||
raise DecodeError('There was an error decoding the token: %s' % e)
|
||||
|
||||
|
||||
def build_argparser():
|
||||
|
||||
usage = '''
|
||||
Encodes or decodes JSON Web Tokens based on input.
|
||||
|
||||
%(prog)s [options] <command> [options] input
|
||||
|
||||
Decoding examples:
|
||||
|
||||
%(prog)s --key=secret decode json.web.token
|
||||
%(prog)s decode --no-verify json.web.token
|
||||
|
||||
Encoding requires the key option and takes space separated key/value pairs
|
||||
separated by equals (=) as input. Examples:
|
||||
|
||||
%(prog)s --key=secret encode iss=me exp=1302049071
|
||||
%(prog)s --key=secret encode foo=bar exp=+10
|
||||
|
||||
The exp key is special and can take an offset to current Unix time.
|
||||
'''
|
||||
|
||||
arg_parser = argparse.ArgumentParser(
|
||||
prog='pyjwt',
|
||||
version='%s %s' % (__package__, __version__),
|
||||
usage=usage
|
||||
)
|
||||
|
||||
p.add_option(
|
||||
'-n', '--no-verify',
|
||||
action='store_false',
|
||||
dest='verify',
|
||||
default=True,
|
||||
help='ignore signature and claims verification on decode'
|
||||
arg_parser.add_argument(
|
||||
'-v', '--version',
|
||||
action='version',
|
||||
version='%(prog)s ' + __version__
|
||||
)
|
||||
|
||||
p.add_option(
|
||||
arg_parser.add_argument(
|
||||
'--key',
|
||||
dest='key',
|
||||
metavar='KEY',
|
||||
|
@ -51,7 +108,7 @@ The exp key is special and can take an offset to current Unix time.\
|
|||
help='set the secret key to sign with'
|
||||
)
|
||||
|
||||
p.add_option(
|
||||
arg_parser.add_argument(
|
||||
'--alg',
|
||||
dest='algorithm',
|
||||
metavar='ALG',
|
||||
|
@ -59,78 +116,47 @@ The exp key is special and can take an offset to current Unix time.\
|
|||
help='set crypto algorithm to sign with. default=HS256'
|
||||
)
|
||||
|
||||
options, arguments = p.parse_args()
|
||||
subparsers = arg_parser.add_subparsers(
|
||||
title='PyJWT subcommands',
|
||||
description='valid subcommands',
|
||||
help='additional help'
|
||||
)
|
||||
|
||||
if len(arguments) > 0 or not sys.stdin.isatty():
|
||||
if len(arguments) == 1 and (not options.verify or options.key):
|
||||
# Try to decode
|
||||
try:
|
||||
if not sys.stdin.isatty():
|
||||
token = sys.stdin.read()
|
||||
else:
|
||||
token = arguments[0]
|
||||
# Encode subcommand
|
||||
encode_parser = subparsers.add_parser('encode', help='use to encode a supplied payload')
|
||||
|
||||
token = token.encode('utf-8')
|
||||
data = decode(token, key=options.key, verify=options.verify)
|
||||
payload_help = """Payload to encode. Must be a space separated list of key/value
|
||||
pairs separated by equals (=) sign."""
|
||||
|
||||
print(json.dumps(data))
|
||||
sys.exit(0)
|
||||
except DecodeError as e:
|
||||
print(e)
|
||||
sys.exit(1)
|
||||
encode_parser.add_argument('payload', nargs='+', help=payload_help)
|
||||
encode_parser.set_defaults(func=encode_payload)
|
||||
|
||||
# Try to encode
|
||||
if options.key is None:
|
||||
print('Key is required when encoding. See --help for usage.')
|
||||
sys.exit(1)
|
||||
# Decode subcommand
|
||||
decode_parser = subparsers.add_parser('decode', help='use to decode a supplied JSON web token')
|
||||
decode_parser.add_argument('token', help='JSON web token to decode.')
|
||||
|
||||
# Build payload object to encode
|
||||
payload = {}
|
||||
decode_parser.add_argument(
|
||||
'-n', '--no-verify',
|
||||
action='store_false',
|
||||
dest='verify',
|
||||
default=True,
|
||||
help='ignore signature and claims verification on decode'
|
||||
)
|
||||
|
||||
for arg in arguments:
|
||||
try:
|
||||
k, v = arg.split('=', 1)
|
||||
decode_parser.set_defaults(func=decode_payload)
|
||||
|
||||
# exp +offset special case?
|
||||
if k == 'exp' and v[0] == '+' and len(v) > 1:
|
||||
v = str(int(time.time()+int(v[1:])))
|
||||
|
||||
# Cast to integer?
|
||||
if v.isdigit():
|
||||
v = int(v)
|
||||
else:
|
||||
# Cast to float?
|
||||
try:
|
||||
v = float(v)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
# Cast to true, false, or null?
|
||||
constants = {'true': True, 'false': False, 'null': None}
|
||||
|
||||
if v in constants:
|
||||
v = constants[v]
|
||||
|
||||
payload[k] = v
|
||||
except ValueError:
|
||||
print('Invalid encoding input at {}'.format(arg))
|
||||
sys.exit(1)
|
||||
|
||||
try:
|
||||
token = encode(
|
||||
payload,
|
||||
key=options.key,
|
||||
algorithm=options.algorithm
|
||||
)
|
||||
|
||||
print(token)
|
||||
sys.exit(0)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
sys.exit(1)
|
||||
else:
|
||||
p.print_help()
|
||||
return arg_parser
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
def main():
|
||||
arg_parser = build_argparser()
|
||||
|
||||
try:
|
||||
arguments = arg_parser.parse_args(sys.argv[1:])
|
||||
|
||||
output = arguments.func(arguments)
|
||||
|
||||
print(output)
|
||||
except Exception as e:
|
||||
print('There was an unforseen error: ', e)
|
||||
arg_parser.print_help()
|
||||
|
|
|
@ -0,0 +1,127 @@
|
|||
|
||||
import argparse
|
||||
import json
|
||||
import sys
|
||||
|
||||
import jwt
|
||||
from jwt.__main__ import build_argparser, decode_payload, encode_payload, main
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
class TestCli:
|
||||
|
||||
def test_build_argparse(self):
|
||||
args = ['--key', '1234', 'encode', 'name=Vader']
|
||||
parser = build_argparser()
|
||||
parsed_args = parser.parse_args(args)
|
||||
|
||||
assert parsed_args.key == '1234'
|
||||
|
||||
def test_encode_payload_raises_value_error_key_is_required(self):
|
||||
encode_args = ['encode', 'name=Vader', 'job=Sith']
|
||||
parser = build_argparser()
|
||||
|
||||
args = parser.parse_args(encode_args)
|
||||
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
encode_payload(args)
|
||||
|
||||
assert 'Key is required when encoding' in str(excinfo.value)
|
||||
|
||||
def test_decode_payload_raises_decoded_error(self):
|
||||
decode_args = ['--key', '1234', 'decode', 'wrong-token']
|
||||
parser = build_argparser()
|
||||
|
||||
args = parser.parse_args(decode_args)
|
||||
|
||||
with pytest.raises(jwt.DecodeError) as excinfo:
|
||||
decode_payload(args)
|
||||
|
||||
assert 'There was an error decoding the token' in str(excinfo.value)
|
||||
|
||||
def test_decode_payload_raises_decoded_error_isatty(self, monkeypatch):
|
||||
def patched_sys_stdin_read():
|
||||
raise jwt.DecodeError()
|
||||
|
||||
decode_args = ['--key', '1234', 'decode', 'wrong-token']
|
||||
parser = build_argparser()
|
||||
|
||||
args = parser.parse_args(decode_args)
|
||||
|
||||
monkeypatch.setattr(sys.stdin, 'isatty', lambda: True)
|
||||
monkeypatch.setattr(sys.stdin, 'read', patched_sys_stdin_read)
|
||||
|
||||
with pytest.raises(jwt.DecodeError) as excinfo:
|
||||
decode_payload(args)
|
||||
|
||||
assert 'There was an error decoding the token' 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'),
|
||||
])
|
||||
def test_encode_decode(self, key, name, job, exp, verify):
|
||||
encode_args = [
|
||||
'--key={0}'.format(key),
|
||||
'encode',
|
||||
'name={0}'.format(name),
|
||||
'job={0}'.format(job),
|
||||
]
|
||||
if exp:
|
||||
encode_args.append('exp={0}'.format(exp))
|
||||
if verify:
|
||||
encode_args.append('verify={0}'.format(verify))
|
||||
|
||||
parser = build_argparser()
|
||||
parsed_encode_args = parser.parse_args(encode_args)
|
||||
token = encode_payload(parsed_encode_args)
|
||||
assert token is not None
|
||||
assert token is not ''
|
||||
|
||||
decode_args = [
|
||||
'--key={0}'.format(key),
|
||||
'decode',
|
||||
token
|
||||
]
|
||||
parser = build_argparser()
|
||||
parsed_decode_args = parser.parse_args(decode_args)
|
||||
|
||||
actual = json.loads(decode_payload(parsed_decode_args))
|
||||
expected = {
|
||||
'job': job,
|
||||
'name': name,
|
||||
}
|
||||
assert actual['name'] == expected['name']
|
||||
assert actual['job'] == expected['job']
|
||||
|
||||
@pytest.mark.parametrize('key,name,job,exp,verify', [
|
||||
('1234', 'Vader', 'Sith', None, None),
|
||||
('4567', 'Anakin', 'Jedi', '+1', None),
|
||||
('4321', 'Padme', 'Queen', '4070926800', 'true'),
|
||||
])
|
||||
def test_main(self, monkeypatch, key, name, job, exp, verify):
|
||||
args = [
|
||||
'test_cli.py',
|
||||
'--key={0}'.format(key),
|
||||
'encode',
|
||||
'name={0}'.format(name),
|
||||
'job={0}'.format(job),
|
||||
]
|
||||
if exp:
|
||||
args.append('exp={0}'.format(exp))
|
||||
if verify:
|
||||
args.append('verify={0}'.format(verify))
|
||||
monkeypatch.setattr(sys, 'argv', args)
|
||||
main()
|
||||
|
||||
def test_main_throw_exception(self, monkeypatch, capsys):
|
||||
def patched_argparser_parse_args(self, args):
|
||||
raise Exception('NOOOOOOOOOOO!')
|
||||
|
||||
monkeypatch.setattr(argparse.ArgumentParser, 'parse_args', patched_argparser_parse_args)
|
||||
main()
|
||||
out, _ = capsys.readouterr()
|
||||
|
||||
assert 'NOOOOOOOOOOO!' in out
|
Loading…
Reference in New Issue