Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Keep auth header untouched #178

Merged
merged 4 commits into from
Dec 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions mock_tests/test_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,42 @@ def test_refresh(weaviate_auth_mock):
client.schema.delete_all() # some call that includes authorization


def test_auth_header_without_weaviate_auth(weaviate_mock):
"""Test that setups that use the Authorization header to authorize to non-weaviate servers."""
bearer_token = "OTHER TOKEN"
weaviate_mock.expect_request(
"/v1/schema", headers={"Authorization": "Bearer " + bearer_token}
).respond_with_json({})

client = weaviate.Client(
url=MOCK_SERVER_URL,
additional_headers={"Authorization": "Bearer " + bearer_token},
)
client.schema.delete_all() # some call that includes authorization


def test_auth_header_with_catchall_proxy(weaviate_mock, recwarn):
"""Test that the client can handle situations in which a proxy returns a catchall page for all requests."""
bearer_token = "OTHER TOKEN"
weaviate_mock.expect_request(
"/v1/schema", headers={"Authorization": "Bearer " + bearer_token}
).respond_with_json({})
weaviate_mock.expect_request("/v1/.well-known/openid-configuration").respond_with_data(
"JsonCannotParseThis"
)

client = weaviate.Client(
url=MOCK_SERVER_URL,
additional_headers={"Authorization": "Bearer " + bearer_token},
)
client.schema.delete_all() # some call that includes authorization

assert len(recwarn) == 1
w = recwarn.pop()
assert issubclass(w.category, UserWarning)
assert str(w.message).startswith("Auth005")


def test_missing_scope(weaviate_auth_mock):
with pytest.raises(MissingScopeException):
weaviate.Client(
Expand Down
22 changes: 15 additions & 7 deletions weaviate/connect/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@
import datetime
import os
import time

from requests.exceptions import JSONDecodeError
from numbers import Real
from threading import Thread, Event
from typing import Any, Dict, Tuple, Optional, Union

import requests
from authlib.integrations.requests_client import OAuth2Session

from weaviate import auth
from weaviate.auth import AuthCredentials, AuthClientCredentials
from weaviate.connect.authentication import _Auth
from weaviate.exceptions import AuthenticationFailedException, UnexpectedStatusCodeException
Expand Down Expand Up @@ -89,10 +90,7 @@ def __init__(

# auth secrets can contain more information than a header (refresh tokens and lifetime) and therefore take
# precedent over headers
if "authorization" in self._headers and auth_client_secret is None:
bearer_header = self._headers["authorization"]
auth_client_secret = auth.AuthBearerToken(access_token=bearer_header)
elif "authorization" in self._headers and auth_client_secret is not None:
if "authorization" in self._headers and auth_client_secret is not None:
_Warnings.auth_header_and_auth_secret()
self._headers.pop("authorization")

Expand All @@ -110,15 +108,25 @@ def _create_session(self, auth_client_secret: Optional[AuthCredentials]) -> None
ValueError
If no authentication credentials provided but the Weaviate server has OpenID configured.
"""
oidc_url = self.url + self._api_version_path + "/.well-known/openid-configuration"
response = requests.get(
self.url + self._api_version_path + "/.well-known/openid-configuration",
oidc_url,
headers={"content-type": "application/json"},
timeout=self._timeout_config,
proxies=self._proxies,
)
if response.status_code == 200:
if auth_client_secret is not None:
# Some setups are behind proxies that return some default page - for example a login - for all requests.
# If the response is not json, we assume that this is the case and try unauthenticated access. Any auth
# header provided by the user is unaffected.
try:
resp = response.json()
except JSONDecodeError:
_Warnings.auth_cannot_parse_oidc_config(oidc_url)
self._session = requests.Session()
return
dirkkul marked this conversation as resolved.
Show resolved Hide resolved

if auth_client_secret is not None:
_auth = _Auth(resp, auth_client_secret, self)
self._session = _auth.get_auth_session()

Expand Down
9 changes: 9 additions & 0 deletions weaviate/warnings.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,15 @@ def auth_header_and_auth_secret():
"""
warnings.warn(message=msg, category=UserWarning, stacklevel=1)

@staticmethod
def auth_cannot_parse_oidc_config(url: str):
msg = f"""Auth005: Could not parse Weaviates OIDC configuration, using unauthenticated access. If you added
an authorization header yourself it will be unaffected.

This can happen if weaviate is miss-configured or you have a proxy inbetween the client and weaviate.
You can test this by visiting {url}."""
warnings.warn(message=msg, category=UserWarning, stacklevel=1)

@staticmethod
def weaviate_server_older_than_1_14(server_version: str):
warnings.warn(
Expand Down