diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 3f3d98d24..f425ecae2 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -130,7 +130,6 @@ jobs: env | sort - name: Run Tests run: make stop ${{ matrix.test-case }} - continue-on-error: ${{ matrix.test-case == 'test-coverage-only' || matrix.allow-failure }} # manually invoke reporting in case of test failure to still generate them # otherwise, they would have been generated automatically following the successful coverage run diff --git a/CHANGES.rst b/CHANGES.rst index 18710d029..dd76df4d8 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -16,7 +16,8 @@ Changes: Fixes: ------ -- No change. +- Fix ``weaver.cli.RequestAuthHandler`` and its derived classes erroneously invoking ``request_auth`` method when + both the ``url`` and ``token`` are omitted, leading to invalid ``requests`` call under ``weaver.utils.request_extra``. .. _changes_6.1.1: diff --git a/tests/test_cli.py b/tests/test_cli.py index c6f8464a9..411f9bca3 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -27,6 +27,7 @@ WeaverClient, main as weaver_cli ) +from weaver.exceptions import AuthenticationError from weaver.formats import ContentEncoding, ContentType @@ -408,7 +409,7 @@ def test_auth_handler_basic(): def test_auth_handler_bearer(): req = WebTestRequest({}) - auth = BearerAuthHandler(identity=str(uuid.uuid4())) + auth = BearerAuthHandler(identity=str(uuid.uuid4()), url="https://example.com") token = str(uuid.uuid4()) with mock.patch( "requests.Session.request", @@ -435,7 +436,7 @@ def test_auth_handler_bearer_explicit_token_matches_request_token(): req_request_token = WebTestRequest({}) token = str(uuid.uuid4()) auth_explicit_token = BearerAuthHandler(token=token) - auth_request_token = BearerAuthHandler(identity=str(uuid.uuid4())) + auth_request_token = BearerAuthHandler(identity=str(uuid.uuid4()), url="https://example.com") with mock.patch( "requests.Session.request", side_effect=lambda *_, **__: mocked_auth_response("access_token", token) @@ -450,7 +451,7 @@ def test_auth_handler_bearer_explicit_token_matches_request_token(): def test_auth_handler_cookie(): req = WebTestRequest({}) - auth = CookieAuthHandler(identity=str(uuid.uuid4())) + auth = CookieAuthHandler(identity=str(uuid.uuid4()), url="https://example.com") token = str(uuid.uuid4()) with mock.patch( "requests.Session.request", @@ -506,7 +507,7 @@ def test_auth_handler_cookie_explicit_token_matches_request_token(): req_request_token = WebTestRequest({}) token = str(uuid.uuid4()) auth_explicit_token = CookieAuthHandler(token=token) - auth_request_token = CookieAuthHandler(identity=str(uuid.uuid4())) + auth_request_token = CookieAuthHandler(identity=str(uuid.uuid4()), url="https://example.com") with mock.patch( "requests.Session.request", side_effect=lambda *_, **__: mocked_auth_response("access_token", token) @@ -519,6 +520,30 @@ def test_auth_handler_cookie_explicit_token_matches_request_token(): assert resp_explicit_token.headers["Cookie"] == resp_request_token.headers["Cookie"] +def test_auth_request_handler_no_url_or_token_init(): + with pytest.raises(AuthenticationError): + BearerAuthHandler(identity=str(uuid.uuid4())) + + try: + BearerAuthHandler(token=str(uuid.uuid4())) # OK + BearerAuthHandler(url="https://example.com") # OK + except Exception as exc: + pytest.fail(msg=f"Expected no init error from valid combinations. Got [{exc}]") + + +def test_auth_request_handler_no_url_ignored_request(): + req = WebTestRequest({}) + auth = BearerAuthHandler( + identity=str(uuid.uuid4()), + url="https://example.com", # URL must be passed to avoid error + ) + auth.url = None # reset after init check + with mock.patch("requests.Session.request") as mock_request: + resp = auth(req) # type: ignore + mock_request.assert_not_called() + assert not resp.headers, "No headers should have been added since URL could not be resolved." + + def test_upload_file_not_found(): with tempfile.NamedTemporaryFile() as tmp_file_deleted: pass # delete on close diff --git a/weaver/cli.py b/weaver/cli.py index dbfcec97f..7ad33715a 100644 --- a/weaver/cli.py +++ b/weaver/cli.py @@ -23,7 +23,7 @@ from weaver import __meta__ from weaver.datatype import AutoBase -from weaver.exceptions import PackageRegistrationError +from weaver.exceptions import AuthenticationError, PackageRegistrationError from weaver.execute import ExecuteMode, ExecuteResponse, ExecuteReturnPreference, ExecuteTransmissionMode from weaver.formats import ContentEncoding, ContentType, OutputFormat, get_content_type, get_format, repr_json from weaver.processes.constants import ProcessSchema @@ -278,6 +278,9 @@ def __init__( HTTPBasicAuth.__init__(self, username=identity, password=password) self.token = token + if not self.token and not self.url: + raise AuthenticationError("Either the token or the URL to retrieve it must be provided to the handler.") + @property def auth_token_name(self): # type: () -> str @@ -334,12 +337,12 @@ def response_parser(self, response): def __call__(self, request): # type: (AnyRequestType) -> AnyRequestType - if self.token is None: + if self.token is None and self.url: auth_token = self.request_auth() else: auth_token = self.token if not auth_token: - LOGGER.warning("Expected authorization token could not be retrieved from: [%s] in [%s]", + LOGGER.warning("Expected authorization token could not be retrieved from URL: [%s] in [%s]", self.url, fully_qualified_name(self)) else: auth_token = self.parse_token(auth_token) diff --git a/weaver/exceptions.py b/weaver/exceptions.py index 9140e1c9c..6e683ad41 100644 --- a/weaver/exceptions.py +++ b/weaver/exceptions.py @@ -55,6 +55,12 @@ class WeaverExecutionError(WeaverException): """ +class AuthenticationError(WeaverException): + """ + Generic exception related to an issue about authentication configuration or resolution. + """ + + class ListingInvalidParameter(WeaverException, OWSInvalidParameterValue, ValueError): """ Error related to an invalid parameter for listing queries. @@ -265,7 +271,7 @@ class PackageRegistrationError(HTTPInternalServerError, OWSNoApplicableCode, Pac """ -class PackageAuthenticationError(HTTPForbidden, OWSAccessForbidden, PackageException): +class PackageAuthenticationError(HTTPForbidden, OWSAccessForbidden, PackageException, AuthenticationError): """ Error related to a runtime failure caused by failing authentication prerequisite.