From bd0a54897f29df8cacfc685db6d60e74247e8e347c7902e1845b7890aec56ea7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Chv=C3=A1tal?= Date: Wed, 13 Feb 2019 09:20:18 +0000 Subject: [PATCH] Accepting request 674430 from home:cbosdonnat:branches:devel:languages:python - Add JWT token plugin feature. Added patches: u_added_jwt_tokens_capability.patch PyJWT-token-plugin.patch: fate#325762 OBS-URL: https://build.opensuse.org/request/show/674430 OBS-URL: https://build.opensuse.org/package/show/devel:languages:python/python-websockify?expand=0&rev=33 --- PyJWT-token-plugin.patch | 53 +++++++++++++++++++++ python-websockify.changes | 8 ++++ python-websockify.spec | 17 ++++++- u_added_jwt_tokens_capability.patch | 71 +++++++++++++++++++++++++++++ 4 files changed, 148 insertions(+), 1 deletion(-) create mode 100644 PyJWT-token-plugin.patch create mode 100644 u_added_jwt_tokens_capability.patch diff --git a/PyJWT-token-plugin.patch b/PyJWT-token-plugin.patch new file mode 100644 index 0000000..cbd12e6 --- /dev/null +++ b/PyJWT-token-plugin.patch @@ -0,0 +1,53 @@ +From cf2bd3930d8506dbb4db172c89a6a628134595aa Mon Sep 17 00:00:00 2001 +From: UXabre +Date: Fri, 21 Dec 2018 03:21:29 -0500 +Subject: [PATCH] Added JWT tokens capability + +--- + setup.py | 2 +- + tests/jwt.secret | 1 + + tests/jwt_rsa | 27 ++++++++++++++++++ + tests/jwt_rsa.pub | 1 + + tests/test_websocketproxy.py | 54 ++++++++++++++++++++++++++++++++++++ + websockify/token_plugins.py | 16 +++++++++++ + 6 files changed, 100 insertions(+), 1 deletion(-) + create mode 100644 tests/jwt.secret + create mode 100644 tests/jwt_rsa + create mode 100644 tests/jwt_rsa.pub + +Index: websockify-0.8.0/setup.py +=================================================================== +--- websockify-0.8.0.orig/setup.py ++++ websockify-0.8.0/setup.py +@@ -34,7 +34,7 @@ setup(name=name, + + packages=['websockify'], + include_package_data=True, +- install_requires=['numpy'], ++ install_requires=['numpy', 'PyJWT', 'cryptography'], + zip_safe=False, + entry_points={ + 'console_scripts': [ +Index: websockify-0.8.0/websockify/token_plugins.py +=================================================================== +--- websockify-0.8.0.orig/websockify/token_plugins.py ++++ websockify-0.8.0/websockify/token_plugins.py +@@ -125,5 +125,17 @@ class JWTTokenApi(BasePlugin): + print >>sys.stderr, "Failed to parse token: %s" % (e) + return None + except ImportError as e: +- print >>sys.stderr, "package jwcrypto not found, are you sure you've installed it correctly?" ++ try: ++ import jwt ++ ++ secret = open(self.source, 'rb').read() ++ parsed = jwt.decode(token, secret, algorithms=['RS256', 'RS384', 'RS512', 'HS256', 'HS384', 'HS512', 'ES256', 'ES384', 'ES512', 'PS256', 'PS384', 'PS512']) ++ ++ return (parsed['host'], parsed['port']) ++ except Exception as e: ++ print >>sys.stderr, "Failed to parse token: %s" % (e) ++ return None ++ except ImportError: ++ print >>sys.stderr, "neither package jwcrypto nor PyJWT found, are you sure you've installed one of them correctly?" ++ return None + return None diff --git a/python-websockify.changes b/python-websockify.changes index 0a9cd94..cefd3b9 100644 --- a/python-websockify.changes +++ b/python-websockify.changes @@ -1,3 +1,11 @@ +------------------------------------------------------------------- +Tue Feb 12 14:34:16 UTC 2019 - Cédric Bosdonnat + +- Add JWT token plugin feature. + Added patches: + u_added_jwt_tokens_capability.patch + PyJWT-token-plugin.patch: fate#325762 + ------------------------------------------------------------------- Tue Dec 4 12:56:00 UTC 2018 - Matej Cepl diff --git a/python-websockify.spec b/python-websockify.spec index a7f9176..d4bd8b1 100644 --- a/python-websockify.spec +++ b/python-websockify.spec @@ -1,7 +1,7 @@ # # spec file for package python-websockify # -# Copyright (c) 2018 SUSE LINUX GmbH, Nuernberg, Germany. +# Copyright (c) 2019 SUSE LINUX GmbH, Nuernberg, Germany. # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -29,6 +29,10 @@ Source: https://files.pythonhosted.org/packages/source/w/websockify/webs Patch1: u_Add-support-for-inetd.patch # PATCH-FEATURE-UPSTREAM u_Fix-inetd-mode-on-python-2.patch fate#323880 msrb@suse.com -- https://github.com/novnc/websockify/pull/293 Patch2: u_Fix-inetd-mode-on-python-2.patch +# PATCH-FEATURE-ALMOST-UPSTREAM u_added_jwt_tokens_capability.patch fate#325762 cbosdonnat@suse.com -- https://github.com/novnc/websockify/pull/372 +Patch3: u_added_jwt_tokens_capability.patch +# PATCH-FIX-OPENSUSE PyJWT-token-plugin.patch fate#325762 cbosdonnat@suse.com -- use PyJWT if jwcrypto is missing +Patch4: PyJWT-token-plugin.patch BuildRequires: %{python_module setuptools} BuildRequires: python-rpm-macros BuildRequires: unzip @@ -40,6 +44,15 @@ BuildArch: noarch %if 0%{?suse_version} Recommends: python-numpy %endif +# SLES 12 and up to 15SP1 doesn't have python-jwcrypto package and will fallback to +# the PyJWT implementation. However opensuse has jwcrypto since 42.3: use this one +# since it also provides support for JWE (encrypted JWT). +%if 0%{?sle_version} +Recommends: python-PyJWT +Recommends: python-cryptography +%else +Recommends: python-jwcrypto +%endif %python_subpackages %description @@ -71,6 +84,8 @@ This package contains common files. %setup -q -n websockify-%{version} %patch1 -p1 %patch2 -p1 +%patch3 -p1 +%patch4 -p1 # remove unwanted shebang sed -i '1 { /^#!/ d }' websockify/websocket*.py # drop unneeded executable bit diff --git a/u_added_jwt_tokens_capability.patch b/u_added_jwt_tokens_capability.patch new file mode 100644 index 0000000..75e3cd8 --- /dev/null +++ b/u_added_jwt_tokens_capability.patch @@ -0,0 +1,71 @@ +From 6dc9005930798873ffa714d184312aafd0209503 Mon Sep 17 00:00:00 2001 +From: UXabre +Date: Thu, 17 Jan 2019 08:53:01 -0500 +Subject: [PATCH] Added JWT/JWS/JWE tokens capability + +--- + test-requirements.txt | 1 + + tests/fixtures/private.pem | 27 +++++++++++ + tests/fixtures/public.pem | 9 ++++ + tests/fixtures/symmetric.key | 1 + + tests/test_websocketproxy.py | 90 ++++++++++++++++++++++++++++++++++++ + websockify/token_plugins.py | 46 ++++++++++++++++++ + 6 files changed, 174 insertions(+) + create mode 100644 tests/fixtures/private.pem + create mode 100644 tests/fixtures/public.pem + create mode 100644 tests/fixtures/symmetric.key + +diff --git a/websockify/token_plugins.py b/websockify/token_plugins.py +index e87dcd0..45e974c 100644 +--- a/websockify/token_plugins.py ++++ b/websockify/token_plugins.py +@@ -87,3 +87,49 @@ class JSONTokenApi(BaseTokenAPI): + def process_result(self, resp): + resp_json = resp.json() + return (resp_json['host'], resp_json['port']) ++ ++ ++class JWTTokenApi(BasePlugin): ++ # source is a JWT-token, with hostname and port included ++ # Both JWS as JWE tokens are accepted. With regards to JWE tokens, the key is re-used for both validation and decryption. ++ ++ def lookup(self, token): ++ try: ++ from jwcrypto import jwt ++ import json ++ ++ key = jwt.JWK() ++ ++ try: ++ with open(self.source, 'rb') as key_file: ++ key_data = key_file.read() ++ except Exception as e: ++ print >>sys.stderr, "Error loading key file: %s" % (e) ++ return None ++ ++ try: ++ key.import_from_pem(key_data) ++ except: ++ try: ++ key.import_key(k=key_data,kty='oct') ++ except: ++ print >>sys.stderr, 'Failed to correctly parse key data!' ++ return None ++ ++ try: ++ token = jwt.JWT(key=key, jwt=token) ++ parsed_header = json.loads(token.header) ++ ++ if 'enc' in parsed_header: ++ # Token is encrypted, so we need to decrypt by passing the claims to a new instance ++ token = jwt.JWT(key=key, jwt=token.claims) ++ ++ parsed = json.loads(token.claims) ++ ++ return (parsed['host'], parsed['port']) ++ except Exception as e: ++ print >>sys.stderr, "Failed to parse token: %s" % (e) ++ return None ++ except ImportError as e: ++ print >>sys.stderr, "package jwcrypto not found, are you sure you've installed it correctly?" ++ return None