From dd7ec05b6f0cd89ede9cc01ee2ec27769ff93cfa Mon Sep 17 00:00:00 2001 From: Anindya Roy Date: Wed, 21 Feb 2024 11:02:39 +0530 Subject: [PATCH 1/8] NXDRIVE-2901: Authorization Error for OAuth --- nxdrive/auth/oauth2.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/nxdrive/auth/oauth2.py b/nxdrive/auth/oauth2.py index 6a03a8a389..1967911f21 100644 --- a/nxdrive/auth/oauth2.py +++ b/nxdrive/auth/oauth2.py @@ -19,6 +19,9 @@ def __init__(self, *args: Any, dao: "BaseDAO" = None, **kwargs: Any) -> None: super().__init__(*args, **kwargs) self._dao = dao + subclient_kwargs = kwargs.get("subclient_kwargs") + subclient_kwargs = {} if subclient_kwargs is None else subclient_kwargs + subclient_kwargs["verify"] = get_verify() self.auth = OAuth2( self.url, client_id=Options.oauth2_client_id, @@ -28,7 +31,7 @@ def __init__(self, *args: Any, dao: "BaseDAO" = None, **kwargs: Any) -> None: redirect_uri=Options.oauth2_redirect_uri, token_endpoint=Options.oauth2_token_endpoint, token=self.token, - subclient_kwargs=kwargs.get("subclient_kwargs"), + subclient_kwargs=subclient_kwargs, ) def connect_url(self) -> str: @@ -49,7 +52,6 @@ def get_token(self, **kwargs: Any) -> "Token": code_verifier=kwargs["code_verifier"], code=kwargs["code"], state=kwargs["state"], - verify=get_verify(), ) self.token = token From ecf8820923e5982e24ea81ae502a6849fd4653a0 Mon Sep 17 00:00:00 2001 From: Anindya Roy Date: Wed, 21 Feb 2024 14:28:42 +0530 Subject: [PATCH 2/8] NXDRIVE-2901: Authorization Error for OAuth --added testcase --- docs/changes/5.5.0.md | 1 + tests/functional/test_oauth2.py | 9 +++++++++ 2 files changed, 10 insertions(+) create mode 100644 tests/functional/test_oauth2.py diff --git a/docs/changes/5.5.0.md b/docs/changes/5.5.0.md index ba7087e5df..359e59a91f 100644 --- a/docs/changes/5.5.0.md +++ b/docs/changes/5.5.0.md @@ -5,6 +5,7 @@ Release date: `2024-xx-xx` ## Core - [NXDRIVE-2882](https://jira.nuxeo.com/browse/NXDRIVE-2882): fix_db should create dump.sql in same dir as db +- [NXDRIVE-2901](https://jira.nuxeo.com/browse/NXDRIVE-2901): Authorization Error for OAuth - [NXDRIVE-2](https://jira.nuxeo.com/browse/NXDRIVE-2): ### Direct Edit diff --git a/tests/functional/test_oauth2.py b/tests/functional/test_oauth2.py new file mode 100644 index 0000000000..1cc0c656e7 --- /dev/null +++ b/tests/functional/test_oauth2.py @@ -0,0 +1,9 @@ +from nxdrive.auth import OAuthentication + + +def test_oauthentication(manager_factory, nuxeo_url): + manager, engine = manager_factory() + remote = engine.remote + dao = remote.dao + oauth = OAuthentication(nuxeo_url, dao=dao, device_id=None) + assert oauth From c2026ad3c2c9ada350d8af73077ab2450ec4b7f3 Mon Sep 17 00:00:00 2001 From: Anindya Roy Date: Wed, 28 Feb 2024 15:22:28 +0530 Subject: [PATCH 3/8] NXDRIVE-2901: Authorization Error for OAuth --PR comment --- nxdrive/auth/oauth2.py | 6 +++--- nxdrive/direct_edit.py | 4 ++-- nxdrive/engine/watcher/local_watcher.py | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/nxdrive/auth/oauth2.py b/nxdrive/auth/oauth2.py index 1967911f21..cd9266ad9d 100644 --- a/nxdrive/auth/oauth2.py +++ b/nxdrive/auth/oauth2.py @@ -18,10 +18,10 @@ class OAuthentication(Authentication): def __init__(self, *args: Any, dao: "BaseDAO" = None, **kwargs: Any) -> None: super().__init__(*args, **kwargs) + self.verification_needed = get_verify() self._dao = dao - subclient_kwargs = kwargs.get("subclient_kwargs") - subclient_kwargs = {} if subclient_kwargs is None else subclient_kwargs - subclient_kwargs["verify"] = get_verify() + subclient_kwargs = kwargs.get("subclient_kwargs", {}) + subclient_kwargs["verify"] = self.verification_needed self.auth = OAuth2( self.url, client_id=Options.oauth2_client_id, diff --git a/nxdrive/direct_edit.py b/nxdrive/direct_edit.py index 6a1fce7aae..4793c791c5 100644 --- a/nxdrive/direct_edit.py +++ b/nxdrive/direct_edit.py @@ -17,7 +17,7 @@ from nuxeo.models import Blob from requests import codes from watchdog.events import FileSystemEvent -from watchdog.observers import Observer +from watchdog.observers import Observer, api from .client.local import LocalClient from .client.remote_client import Remote @@ -96,7 +96,7 @@ def __init__(self, manager: "Manager", folder: Path, /) -> None: self._event_handler: Optional[DriveFSEventHandler] = None self._metrics = {"edit_files": 0} - self._observer: Observer = None + self._observer: api.BaseObserver = None self.local = LocalClient(self._folder) self._upload_queue: Queue = Queue() self.is_already_locked = False diff --git a/nxdrive/engine/watcher/local_watcher.py b/nxdrive/engine/watcher/local_watcher.py index 19c56a3bff..e16c4be2d8 100644 --- a/nxdrive/engine/watcher/local_watcher.py +++ b/nxdrive/engine/watcher/local_watcher.py @@ -11,7 +11,7 @@ from typing import TYPE_CHECKING, Dict, List, Optional, Set, Tuple from watchdog.events import FileSystemEvent, PatternMatchingEventHandler -from watchdog.observers import Observer +from watchdog.observers import Observer, api from ...client.local import FileInfo from ...constants import LINUX, MAC, ROOT, UNACCESSIBLE_HASH, WINDOWS @@ -90,7 +90,7 @@ def __init__(self, engine: "Engine", dao: "EngineDAO", /) -> None: } self._event_handler: Optional[DriveFSEventHandler] = None - self._observer: Optional[Observer] = None + self._observer: api.BaseObserver = None self._delete_events: Dict[str, Tuple[int, DocPair]] = {} self._folder_scan_events: Dict[Path, Tuple[float, DocPair]] = {} From fd41f70106fbd628c445b4d236b775d65b7e5532 Mon Sep 17 00:00:00 2001 From: Anindya Roy Date: Thu, 21 Mar 2024 21:31:21 +0530 Subject: [PATCH 4/8] NXDRIVE-2901: Authorization Error for OAuth --21/03 001 --- nxdrive/client/remote_client.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/nxdrive/client/remote_client.py b/nxdrive/client/remote_client.py index 80ddb46951..ef7fa60b74 100644 --- a/nxdrive/client/remote_client.py +++ b/nxdrive/client/remote_client.py @@ -289,6 +289,16 @@ def execute(self, /, **kwargs: Any) -> Any: """ # Unauthorized and Forbidden exceptions are handled by the Python client. try: + log.info(f"******** self.token: {self.token!r}") + if self.token: + auth_token = self.auth.auth.token + log.info(f"******** auth.token: {auth_token!r}") + if self.token != auth_token: + log.info( + "******** Tokens are different, new token canbe stored into db" + ) + else: + log.info("******** Tokens are same, no need to store into the db") return self.operations.execute(ssl_verify=Options.ssl_no_verify, **kwargs) except HTTPError as e: if e.status == requests.codes.not_found: From 27101a5341f34fefb90501f420225fd49d385a7f Mon Sep 17 00:00:00 2001 From: Anindya Roy Date: Fri, 22 Mar 2024 15:42:07 +0530 Subject: [PATCH 5/8] NXDRIVE-2901: Authorization Error for OAuth --22/03 001 --- nxdrive/client/remote_client.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/nxdrive/client/remote_client.py b/nxdrive/client/remote_client.py index ef7fa60b74..6c1baa5e73 100644 --- a/nxdrive/client/remote_client.py +++ b/nxdrive/client/remote_client.py @@ -69,6 +69,8 @@ from ..qt.imports import QApplication from ..utils import ( compute_digest, + encrypt, + force_decode, get_current_locale, get_verify, lock_path, @@ -127,6 +129,8 @@ def __init__( f"SSL verify: {verify}-> will be changed to {self.verification_needed}" ) + self.token = token + super().__init__( auth=auth, host=url, @@ -289,17 +293,28 @@ def execute(self, /, **kwargs: Any) -> Any: """ # Unauthorized and Forbidden exceptions are handled by the Python client. try: + resp = self.operations.execute(ssl_verify=Options.ssl_no_verify, **kwargs) log.info(f"******** self.token: {self.token!r}") - if self.token: + if self.token and self.auth: auth_token = self.auth.auth.token log.info(f"******** auth.token: {auth_token!r}") if self.token != auth_token: log.info( "******** Tokens are different, new token canbe stored into db" ) + if self.dao: + remote_user = self.dao.get_config("remote_user") + server_url = self.dao.get_config("server_url") + key = f"{remote_user}{server_url}" + secure_token = force_decode(encrypt(auth_token, key)) + self.dao.update_config("remote_token", secure_token) + log.info("Token Stored Successfully") + else: + log.info("dao is not available") + else: log.info("******** Tokens are same, no need to store into the db") - return self.operations.execute(ssl_verify=Options.ssl_no_verify, **kwargs) + return resp except HTTPError as e: if e.status == requests.codes.not_found: raise NotFound("Response code not found") From dc8d7df05c7c9478ef01a9c572e66dc2613e0028 Mon Sep 17 00:00:00 2001 From: Anindya Roy Date: Fri, 22 Mar 2024 16:24:45 +0530 Subject: [PATCH 6/8] NXDRIVE-2901: Authorization Error for OAuth --22/03 002 --- nxdrive/client/remote_client.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/nxdrive/client/remote_client.py b/nxdrive/client/remote_client.py index 6c1baa5e73..17aa34c8de 100644 --- a/nxdrive/client/remote_client.py +++ b/nxdrive/client/remote_client.py @@ -306,7 +306,12 @@ def execute(self, /, **kwargs: Any) -> Any: remote_user = self.dao.get_config("remote_user") server_url = self.dao.get_config("server_url") key = f"{remote_user}{server_url}" - secure_token = force_decode(encrypt(auth_token, key)) + stored_token = ( + json.dumps(auth_token) + if isinstance(auth_token, dict) + else auth_token + ) + secure_token = force_decode(encrypt(stored_token, key)) self.dao.update_config("remote_token", secure_token) log.info("Token Stored Successfully") else: From a5d234fbd6e68a5aa486d492d0f3f876f88afdd6 Mon Sep 17 00:00:00 2001 From: Anindya Roy Date: Tue, 26 Mar 2024 10:27:17 +0530 Subject: [PATCH 7/8] NXDRIVE-2901: Authorization Error for OAuth --26/03 -Added Test case(s) --- nxdrive/client/remote_client.py | 37 ++++++++----------- tests/functional/test_remote_client.py | 50 ++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 23 deletions(-) diff --git a/nxdrive/client/remote_client.py b/nxdrive/client/remote_client.py index 17aa34c8de..ec3a555cc0 100644 --- a/nxdrive/client/remote_client.py +++ b/nxdrive/client/remote_client.py @@ -294,37 +294,28 @@ def execute(self, /, **kwargs: Any) -> Any: # Unauthorized and Forbidden exceptions are handled by the Python client. try: resp = self.operations.execute(ssl_verify=Options.ssl_no_verify, **kwargs) - log.info(f"******** self.token: {self.token!r}") if self.token and self.auth: auth_token = self.auth.auth.token - log.info(f"******** auth.token: {auth_token!r}") - if self.token != auth_token: - log.info( - "******** Tokens are different, new token canbe stored into db" - ) - if self.dao: - remote_user = self.dao.get_config("remote_user") - server_url = self.dao.get_config("server_url") - key = f"{remote_user}{server_url}" - stored_token = ( - json.dumps(auth_token) - if isinstance(auth_token, dict) - else auth_token - ) - secure_token = force_decode(encrypt(stored_token, key)) - self.dao.update_config("remote_token", secure_token) - log.info("Token Stored Successfully") - else: - log.info("dao is not available") - - else: - log.info("******** Tokens are same, no need to store into the db") + if self.token != auth_token and self.dao: + self._store_token(auth_token) + self.token = auth_token return resp except HTTPError as e: if e.status == requests.codes.not_found: raise NotFound("Response code not found") raise e + def _store_token(self, auth_token): + remote_user = self.dao.get_config("remote_user") + server_url = self.dao.get_config("server_url") + key = f"{remote_user}{server_url}" + stored_token = ( + json.dumps(auth_token) if isinstance(auth_token, dict) else auth_token + ) + secure_token = force_decode(encrypt(stored_token, key)) + self.dao.update_config("remote_token", secure_token) + log.info("Token Stored Successfully") + @staticmethod def escape(path: str, /) -> str: """Escape any problematic character for a NXQL query. diff --git a/tests/functional/test_remote_client.py b/tests/functional/test_remote_client.py index 57034d6cef..168e3f8423 100644 --- a/tests/functional/test_remote_client.py +++ b/tests/functional/test_remote_client.py @@ -262,3 +262,53 @@ def get_download_(*args, **kwargs): with patch.object(remote.dao, "get_download", new=get_download_): returned_val = remote.transfer_end_callback(obj1_) assert not returned_val + + +def test_store_refresh_token(manager_factory): + manager, engine = manager_factory() + remote = engine.remote + remote.token = { + "access_token": "D0QCbs1aJJsPDXzT\ + 1IrC4oKzjbFevn4s", + "refresh_token": "Fch4TbOM8okl8sLajlN\ + 37L8YHKMSfc9cFe7RMVWRG4ctNvBmSvn2SFXg5CtUJKS2", + "token_type": "bearer", + "expires_in": 3239, + "expires_at": 1711427876, + } + remote.auth = Mock() + remote.auth.auth = Mock() + remote.auth.auth.token = remote.token + old_remote_token = remote.token + with manager: + remote.execute(command="UserWorkspace.Get") + assert old_remote_token == remote.token + + remote.auth.auth.token = { + "access_token": "D0QCbs1aJJsPDXzT\ + 1IrC4oKzjbFevn4s", + "refresh_token": "Fch4TbOM8okl8sLajlP\ + 37L8YHKMSfc9cFe7RMVWRG4ctNvBmSvn2SFXg5CtUJKS2", + "token_type": "bearer", + "expires_in": 3239, + "expires_at": 1711427876, + } + old_remote_token = remote.token + with manager: + remote.execute(command="UserWorkspace.Get") + assert remote.token == remote.auth.auth.token + + remote.auth.auth.token = { + "access_token": "D0QCbs1aJJsPDXzT\ + 1IrC4oKzjbFevn4s", + "refresh_token": "Fch4TbOM8okl8sLajlP\ + 37L8YHKMSfc9cFe7RMVWRG4ctNvBmSvn2SFXg5CtUJKS2", + "token_type": "bearer", + "expires_in": 3239, + "expires_at": 1711427876, + } + old_remote_token = remote.token + remote.dao = None + with manager: + remote.execute(command="UserWorkspace.Get") + assert remote.token == old_remote_token From 55f2d9730c7d4205b34df414928bb8ce5ac24d7b Mon Sep 17 00:00:00 2001 From: Anindya Roy Date: Tue, 26 Mar 2024 11:01:02 +0530 Subject: [PATCH 8/8] NXDRIVE-2901: Authorization Error for OAuth --26/03 -Added Test case(s). --- nxdrive/client/remote_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nxdrive/client/remote_client.py b/nxdrive/client/remote_client.py index ec3a555cc0..bc38cd5814 100644 --- a/nxdrive/client/remote_client.py +++ b/nxdrive/client/remote_client.py @@ -305,7 +305,7 @@ def execute(self, /, **kwargs: Any) -> Any: raise NotFound("Response code not found") raise e - def _store_token(self, auth_token): + def _store_token(self, auth_token: Any) -> None: remote_user = self.dao.get_config("remote_user") server_url = self.dao.get_config("server_url") key = f"{remote_user}{server_url}"