diff --git a/openbb_platform/core/openbb_core/app/service/hub_service.py b/openbb_platform/core/openbb_core/app/service/hub_service.py index 419eef955dd1..3448eb8d52cb 100644 --- a/openbb_platform/core/openbb_core/app/service/hub_service.py +++ b/openbb_platform/core/openbb_core/app/service/hub_service.py @@ -4,9 +4,7 @@ from warnings import warn from fastapi import HTTPException -from jose import JWTError -from jose.exceptions import ExpiredSignatureError -from jose.jwt import decode, get_unverified_header +from jwt import ExpiredSignatureError, PyJWTError, decode, get_unverified_header from openbb_core.app.model.abstract.error import OpenBBError from openbb_core.app.model.credentials import Credentials from openbb_core.app.model.hub.hub_session import HubSession @@ -139,7 +137,7 @@ def _get_session_from_platform_token(self, token: str) -> HubSession: if not token: raise OpenBBError("Platform personal access token not found.") - self.check_token_expiration(token) + self._check_token_expiration(token) response = post( url=self._base_url + "/sdk/login", @@ -259,7 +257,7 @@ def platform2hub(self, credentials: Credentials) -> HubUserSettings: return settings @staticmethod - def check_token_expiration(token: str) -> None: + def _check_token_expiration(token: str) -> None: """Check token expiration, raises exception if expired.""" try: header_data = get_unverified_header(token) @@ -271,5 +269,5 @@ def check_token_expiration(token: str) -> None: ) except ExpiredSignatureError as e: raise OpenBBError("Platform personal access token expired.") from e - except JWTError as e: + except PyJWTError as e: raise OpenBBError("Failed to decode Platform token.") from e diff --git a/openbb_platform/core/poetry.lock b/openbb_platform/core/poetry.lock index 80094cd46e13..44a2f785be91 100644 --- a/openbb_platform/core/poetry.lock +++ b/openbb_platform/core/poetry.lock @@ -321,24 +321,6 @@ files = [ {file = "colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44"}, ] -[[package]] -name = "ecdsa" -version = "0.18.0" -description = "ECDSA cryptographic signature library (pure python)" -optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" -files = [ - {file = "ecdsa-0.18.0-py2.py3-none-any.whl", hash = "sha256:80600258e7ed2f16b9aa1d7c295bd70194109ad5a30fdee0eaeefef1d4c559dd"}, - {file = "ecdsa-0.18.0.tar.gz", hash = "sha256:190348041559e21b22a1d65cee485282ca11a6f81d503fddb84d5017e9ed1e49"}, -] - -[package.dependencies] -six = ">=1.9.0" - -[package.extras] -gmpy = ["gmpy"] -gmpy2 = ["gmpy2"] - [[package]] name = "exceptiongroup" version = "1.2.0" @@ -840,17 +822,6 @@ dev = ["black", "flake8", "flake8-print", "isort", "pre-commit"] sentry = ["django", "sentry-sdk"] test = ["coverage", "flake8", "freezegun (==0.3.15)", "mock (>=2.0.0)", "pylint", "pytest", "pytest-timeout"] -[[package]] -name = "pyasn1" -version = "0.5.1" -description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)" -optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" -files = [ - {file = "pyasn1-0.5.1-py2.py3-none-any.whl", hash = "sha256:4439847c58d40b1d0a573d07e3856e95333f1976294494c325775aeca506eb58"}, - {file = "pyasn1-0.5.1.tar.gz", hash = "sha256:6d391a96e59b23130a5cfa74d6fd7f388dbbe26cc8f1edf39fdddf08d9d6676c"}, -] - [[package]] name = "pydantic" version = "2.6.4" @@ -961,6 +932,23 @@ files = [ [package.dependencies] typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" +[[package]] +name = "pyjwt" +version = "2.8.0" +description = "JSON Web Token implementation in Python" +optional = false +python-versions = ">=3.7" +files = [ + {file = "PyJWT-2.8.0-py3-none-any.whl", hash = "sha256:59127c392cc44c2da5bb3192169a91f429924e17aff6534d70fdc02ab3e04320"}, + {file = "PyJWT-2.8.0.tar.gz", hash = "sha256:57e28d156e3d5c10088e0c68abb90bfac3df82b40a71bd0daa20c65ccd5c23de"}, +] + +[package.extras] +crypto = ["cryptography (>=3.4.0)"] +dev = ["coverage[toml] (==5.0.4)", "cryptography (>=3.4.0)", "pre-commit", "pytest (>=6.0.0,<7.0.0)", "sphinx (>=4.5.0,<5.0.0)", "sphinx-rtd-theme", "zope.interface"] +docs = ["sphinx (>=4.5.0,<5.0.0)", "sphinx-rtd-theme", "zope.interface"] +tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"] + [[package]] name = "pytest" version = "7.4.4" @@ -1059,27 +1047,6 @@ files = [ [package.extras] cli = ["click (>=5.0)"] -[[package]] -name = "python-jose" -version = "3.3.0" -description = "JOSE implementation in Python" -optional = false -python-versions = "*" -files = [ - {file = "python-jose-3.3.0.tar.gz", hash = "sha256:55779b5e6ad599c6336191246e95eb2293a9ddebd555f796a65f838f07e5d78a"}, - {file = "python_jose-3.3.0-py2.py3-none-any.whl", hash = "sha256:9b1376b023f8b298536eedd47ae1089bcdb848f1535ab30555cd92002d78923a"}, -] - -[package.dependencies] -ecdsa = "!=0.15" -pyasn1 = "*" -rsa = "*" - -[package.extras] -cryptography = ["cryptography (>=3.4.0)"] -pycrypto = ["pyasn1", "pycrypto (>=2.6.0,<2.7.0)"] -pycryptodome = ["pyasn1", "pycryptodome (>=3.3.1,<4.0.0)"] - [[package]] name = "python-multipart" version = "0.0.7" @@ -1130,7 +1097,6 @@ files = [ {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, - {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef"}, {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, @@ -1186,20 +1152,6 @@ urllib3 = ">=1.21.1,<3" socks = ["PySocks (>=1.5.6,!=1.5.7)"] use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] -[[package]] -name = "rsa" -version = "4.9" -description = "Pure-Python RSA implementation" -optional = false -python-versions = ">=3.6,<4" -files = [ - {file = "rsa-4.9-py3-none-any.whl", hash = "sha256:90260d9058e514786967344d0ef75fa8727eed8a7d2e43ce9f4bcf1b536174f7"}, - {file = "rsa-4.9.tar.gz", hash = "sha256:e38464a49c6c85d7f1351b0126661487a7e0a14a50f1675ec50eb34d4f20ef21"}, -] - -[package.dependencies] -pyasn1 = ">=0.1.3" - [[package]] name = "ruff" version = "0.1.15" @@ -1722,4 +1674,4 @@ testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "p [metadata] lock-version = "2.0" python-versions = "^3.8" -content-hash = "c58b1fc15e472b1609a4194d44da6cd35fc78748cd0931a874bebf441fe8c8ba" +content-hash = "3431ad81cc2788032dfa6b3b7b12d705da384ecc9c592c7663da3d36d885e021" diff --git a/openbb_platform/core/pyproject.toml b/openbb_platform/core/pyproject.toml index 2f6467930f7d..4832836ca77c 100644 --- a/openbb_platform/core/pyproject.toml +++ b/openbb_platform/core/pyproject.toml @@ -13,7 +13,6 @@ websockets = "^12.0" pandas = ">=1.5.3" html5lib = "^1.1" fastapi = "^0.104.1" -python-jose = "^3.3.0" uuid7 = "^0.1.0" posthog = "^3.3.1" python-multipart = "^0.0.7" @@ -23,6 +22,7 @@ importlib-metadata = "^6.8.0" python-dotenv = "^1.0.0" aiohttp = "^3.9.5" ruff = "^0.1.6" +pyjwt = "^2.8.0" [tool.poetry.group.dev.dependencies] pytest = "^7.0.0" diff --git a/openbb_platform/core/tests/app/service/test_hub_service.py b/openbb_platform/core/tests/app/service/test_hub_service.py index db2030d561ec..37cab472d228 100644 --- a/openbb_platform/core/tests/app/service/test_hub_service.py +++ b/openbb_platform/core/tests/app/service/test_hub_service.py @@ -5,9 +5,11 @@ from pathlib import Path +from time import time from unittest.mock import MagicMock, patch import pytest +from jwt import encode from openbb_core.app.service.hub_service import ( Credentials, HubService, @@ -308,3 +310,35 @@ def test_platform2hub(): assert user_settings.features_keys["API_FRED_KEY"] == "fred" assert user_settings.features_keys["benzinga_api_key"] == "benzinga" assert "some_api_key" not in user_settings.features_keys + + +@pytest.mark.parametrize( + "token, message", + [ + # valid + ( + encode( + {"some": "payload", "exp": int(time()) + 100}, + "secret", + algorithm="HS256", + ), + None, + ), + # expired + ( + encode( + {"some": "payload", "exp": int(time())}, "secret", algorithm="HS256" + ), + "Platform personal access token expired.", + ), + # invalid + ("invalid_token", "Failed to decode Platform token."), + ], +) +def test__check_token_expiration(token, message): + """Test check token expiration function.""" + if message: + with pytest.raises(OpenBBError, match=message): + HubService._check_token_expiration(token) + else: + HubService._check_token_expiration(token)