diff --git a/openeo/rest/connection.py b/openeo/rest/connection.py index 5625ef479..ba6f71f9a 100644 --- a/openeo/rest/connection.py +++ b/openeo/rest/connection.py @@ -211,6 +211,37 @@ def _get_refresh_token_store(self) -> RefreshTokenStore: self._refresh_token_store = RefreshTokenStore() return self._refresh_token_store + def list_auth_providers(self) -> list[dict]: + providers = [] + cap = self.capabilities() + + # Add OIDC providers + oidc_path = "/credentials/oidc" + if cap.supports_endpoint(oidc_path, method="GET"): + try: + data = self.get(oidc_path, expected_status=200).json() + if isinstance(data, dict): + for provider in data.get("providers", []): + provider["type"] = "oidc" + providers.append(provider) + except OpenEoApiError as e: + _log.warning(f"Unable to load the OpenID Connect provider list: {e.message}") + + # Add Basic provider + basic_path = "/credentials/basic" + if cap.supports_endpoint(basic_path, method="GET"): + providers.append( + { + "id": basic_path, + "issuer": self.build_url(basic_path), + "type": "basic", + "title": "Internal", + "description": "The HTTP Basic authentication method is mostly used for development and testing purposes.", + } + ) + + return providers + def authenticate_basic(self, username: Optional[str] = None, password: Optional[str] = None) -> Connection: """ Authenticate a user to the backend using basic username and password. diff --git a/tests/rest/test_connection.py b/tests/rest/test_connection.py index 0fc0a3976..a6f885cbc 100644 --- a/tests/rest/test_connection.py +++ b/tests/rest/test_connection.py @@ -767,6 +767,67 @@ def test_create_connection_lazy_refresh_token_store(requests_mock): ) +def test_list_auth_providers(requests_mock, api_version): + requests_mock.get(API_URL, json=build_capabilities(api_version=api_version)) + requests_mock.get( + API_URL + "credentials/oidc", + json={ + "providers": [ + {"id": "p1", "issuer": "https://openeo.example", "title": "openEO", "scopes": ["openid"]}, + {"id": "p2", "issuer": "https://other.example", "title": "Other", "scopes": ["openid"]}, + ] + }, + ) + + conn = Connection(API_URL) + providers = conn.list_auth_providers() + assert len(providers) == 3 + + p1 = next(filter(lambda x: x["id"] == "p1", providers), None) + assert isinstance(p1, dict) + assert p1["type"] == "oidc" + assert p1["issuer"] == "https://openeo.example" + assert p1["title"] == "openEO" + + p2 = next(filter(lambda x: x["id"] == "p2", providers), None) + assert isinstance(p2, dict) + assert p2["type"] == "oidc" + assert p2["issuer"] == "https://other.example" + assert p2["title"] == "Other" + + basic = next(filter(lambda x: x["id"] == "/credentials/basic", providers), None) + assert isinstance(basic, dict) + assert basic["type"] == "basic" + assert basic["issuer"] == API_URL + "credentials/basic" + assert basic["title"] == "Internal" + + +def test_list_auth_providers_empty(requests_mock, api_version): + requests_mock.get( + API_URL, + json=build_capabilities(api_version=api_version, basic_auth=False, oidc_auth=False), + ) + + conn = Connection(API_URL) + providers = conn.list_auth_providers() + assert len(providers) == 0 + + +def test_list_auth_providers_invalid(requests_mock, api_version, caplog): + requests_mock.get(API_URL, json=build_capabilities(api_version=api_version, basic_auth=False)) + error_message = "Maintenance ongoing" + requests_mock.get( + API_URL + "credentials/oidc", + status_code=500, + json={"code": "Internal", "message": error_message}, + ) + + conn = Connection(API_URL) + providers = conn.list_auth_providers() + assert len(providers) == 0 + assert f"Unable to load the OpenID Connect provider list: {error_message}" in caplog.messages + + def test_authenticate_basic_no_support(requests_mock, api_version): requests_mock.get(API_URL, json={"api_version": api_version, "endpoints": []})