Skip to content

Commit d480a2f

Browse files
Revert "Remove deprecated http_auth parameter (elastic#2839)" (elastic#2872)
This reverts commit 74387d0.
1 parent 470896e commit d480a2f

File tree

13 files changed

+287
-8
lines changed

13 files changed

+287
-8
lines changed

elasticsearch/_async/client/__init__.py

+21
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@
8787
_rewrite_parameters,
8888
_stability_warning,
8989
client_node_configs,
90+
is_requests_http_auth,
91+
is_requests_node_class,
9092
)
9193
from .watcher import WatcherClient
9294
from .xpack import XPackClient
@@ -178,6 +180,7 @@ def __init__(
178180
t.Callable[[t.Dict[str, t.Any], NodeConfig], t.Optional[NodeConfig]]
179181
] = None,
180182
meta_header: t.Union[DefaultType, bool] = DEFAULT,
183+
http_auth: t.Union[DefaultType, t.Any] = DEFAULT,
181184
# Internal use only
182185
_transport: t.Optional[AsyncTransport] = None,
183186
) -> None:
@@ -225,9 +228,26 @@ def __init__(
225228
sniff_callback = default_sniff_callback
226229

227230
if _transport is None:
231+
requests_session_auth = None
232+
if http_auth is not None and http_auth is not DEFAULT:
233+
if is_requests_http_auth(http_auth):
234+
# If we're using custom requests authentication
235+
# then we need to alert the user that they also
236+
# need to use 'node_class=requests'.
237+
if not is_requests_node_class(node_class):
238+
raise ValueError(
239+
"Using a custom 'requests.auth.AuthBase' class for "
240+
"'http_auth' must be used with node_class='requests'"
241+
)
242+
243+
# Reset 'http_auth' to DEFAULT so it's not consumed below.
244+
requests_session_auth = http_auth
245+
http_auth = DEFAULT
246+
228247
node_configs = client_node_configs(
229248
hosts,
230249
cloud_id=cloud_id,
250+
requests_session_auth=requests_session_auth,
231251
connections_per_node=connections_per_node,
232252
http_compress=http_compress,
233253
verify_certs=verify_certs,
@@ -314,6 +334,7 @@ def __init__(
314334
self._headers["x-opaque-id"] = opaque_id
315335
self._headers = resolve_auth_headers(
316336
self._headers,
337+
http_auth=http_auth,
317338
api_key=api_key,
318339
basic_auth=basic_auth,
319340
bearer_auth=bearer_auth,

elasticsearch/_async/client/_base.py

+26
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868

6969
def resolve_auth_headers(
7070
headers: Optional[Mapping[str, str]],
71+
http_auth: Union[DefaultType, None, Tuple[str, str], str] = DEFAULT,
7172
api_key: Union[DefaultType, None, Tuple[str, str], str] = DEFAULT,
7273
basic_auth: Union[DefaultType, None, Tuple[str, str], str] = DEFAULT,
7374
bearer_auth: Union[DefaultType, None, str] = DEFAULT,
@@ -77,7 +78,32 @@ def resolve_auth_headers(
7778
elif not isinstance(headers, HttpHeaders):
7879
headers = HttpHeaders(headers)
7980

81+
resolved_http_auth = http_auth if http_auth is not DEFAULT else None
8082
resolved_basic_auth = basic_auth if basic_auth is not DEFAULT else None
83+
if resolved_http_auth is not None:
84+
if resolved_basic_auth is not None:
85+
raise ValueError(
86+
"Can't specify both 'http_auth' and 'basic_auth', "
87+
"instead only specify 'basic_auth'"
88+
)
89+
if isinstance(http_auth, str) or (
90+
isinstance(resolved_http_auth, (list, tuple))
91+
and all(isinstance(x, str) for x in resolved_http_auth)
92+
):
93+
resolved_basic_auth = resolved_http_auth
94+
else:
95+
raise TypeError(
96+
"The deprecated 'http_auth' parameter must be either 'Tuple[str, str]' or 'str'. "
97+
"Use either the 'basic_auth' parameter instead"
98+
)
99+
100+
warnings.warn(
101+
"The 'http_auth' parameter is deprecated. "
102+
"Use 'basic_auth' or 'bearer_auth' parameters instead",
103+
category=DeprecationWarning,
104+
stacklevel=warn_stacklevel(),
105+
)
106+
81107
resolved_api_key = api_key if api_key is not DEFAULT else None
82108
resolved_bearer_auth = bearer_auth if bearer_auth is not DEFAULT else None
83109
if resolved_api_key or resolved_basic_auth or resolved_bearer_auth:

elasticsearch/_async/client/utils.py

+4
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
_rewrite_parameters,
2828
_stability_warning,
2929
client_node_configs,
30+
is_requests_http_auth,
31+
is_requests_node_class,
3032
)
3133

3234
__all__ = [
@@ -41,4 +43,6 @@
4143
"client_node_configs",
4244
"_rewrite_parameters",
4345
"_stability_warning",
46+
"is_requests_http_auth",
47+
"is_requests_node_class",
4448
]

elasticsearch/_async/helpers.py

+5-2
Original file line numberDiff line numberDiff line change
@@ -418,9 +418,12 @@ def pop_transport_kwargs(kw: MutableMapping[str, Any]) -> MutableMapping[str, An
418418
# Grab options that should be propagated to every
419419
# API call within this helper instead of just 'search()'
420420
transport_kwargs = {}
421-
for key in ("headers", "api_key", "basic_auth", "bearer_auth"):
421+
for key in ("headers", "api_key", "http_auth", "basic_auth", "bearer_auth"):
422422
try:
423-
transport_kwargs[key] = kw.pop(key)
423+
value = kw.pop(key)
424+
if key == "http_auth":
425+
key = "basic_auth"
426+
transport_kwargs[key] = value
424427
except KeyError:
425428
pass
426429
return transport_kwargs

elasticsearch/_sync/client/__init__.py

+21
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@
8787
_rewrite_parameters,
8888
_stability_warning,
8989
client_node_configs,
90+
is_requests_http_auth,
91+
is_requests_node_class,
9092
)
9193
from .watcher import WatcherClient
9294
from .xpack import XPackClient
@@ -178,6 +180,7 @@ def __init__(
178180
t.Callable[[t.Dict[str, t.Any], NodeConfig], t.Optional[NodeConfig]]
179181
] = None,
180182
meta_header: t.Union[DefaultType, bool] = DEFAULT,
183+
http_auth: t.Union[DefaultType, t.Any] = DEFAULT,
181184
# Internal use only
182185
_transport: t.Optional[Transport] = None,
183186
) -> None:
@@ -225,9 +228,26 @@ def __init__(
225228
sniff_callback = default_sniff_callback
226229

227230
if _transport is None:
231+
requests_session_auth = None
232+
if http_auth is not None and http_auth is not DEFAULT:
233+
if is_requests_http_auth(http_auth):
234+
# If we're using custom requests authentication
235+
# then we need to alert the user that they also
236+
# need to use 'node_class=requests'.
237+
if not is_requests_node_class(node_class):
238+
raise ValueError(
239+
"Using a custom 'requests.auth.AuthBase' class for "
240+
"'http_auth' must be used with node_class='requests'"
241+
)
242+
243+
# Reset 'http_auth' to DEFAULT so it's not consumed below.
244+
requests_session_auth = http_auth
245+
http_auth = DEFAULT
246+
228247
node_configs = client_node_configs(
229248
hosts,
230249
cloud_id=cloud_id,
250+
requests_session_auth=requests_session_auth,
231251
connections_per_node=connections_per_node,
232252
http_compress=http_compress,
233253
verify_certs=verify_certs,
@@ -314,6 +334,7 @@ def __init__(
314334
self._headers["x-opaque-id"] = opaque_id
315335
self._headers = resolve_auth_headers(
316336
self._headers,
337+
http_auth=http_auth,
317338
api_key=api_key,
318339
basic_auth=basic_auth,
319340
bearer_auth=bearer_auth,

elasticsearch/_sync/client/_base.py

+26
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868

6969
def resolve_auth_headers(
7070
headers: Optional[Mapping[str, str]],
71+
http_auth: Union[DefaultType, None, Tuple[str, str], str] = DEFAULT,
7172
api_key: Union[DefaultType, None, Tuple[str, str], str] = DEFAULT,
7273
basic_auth: Union[DefaultType, None, Tuple[str, str], str] = DEFAULT,
7374
bearer_auth: Union[DefaultType, None, str] = DEFAULT,
@@ -77,7 +78,32 @@ def resolve_auth_headers(
7778
elif not isinstance(headers, HttpHeaders):
7879
headers = HttpHeaders(headers)
7980

81+
resolved_http_auth = http_auth if http_auth is not DEFAULT else None
8082
resolved_basic_auth = basic_auth if basic_auth is not DEFAULT else None
83+
if resolved_http_auth is not None:
84+
if resolved_basic_auth is not None:
85+
raise ValueError(
86+
"Can't specify both 'http_auth' and 'basic_auth', "
87+
"instead only specify 'basic_auth'"
88+
)
89+
if isinstance(http_auth, str) or (
90+
isinstance(resolved_http_auth, (list, tuple))
91+
and all(isinstance(x, str) for x in resolved_http_auth)
92+
):
93+
resolved_basic_auth = resolved_http_auth
94+
else:
95+
raise TypeError(
96+
"The deprecated 'http_auth' parameter must be either 'Tuple[str, str]' or 'str'. "
97+
"Use either the 'basic_auth' parameter instead"
98+
)
99+
100+
warnings.warn(
101+
"The 'http_auth' parameter is deprecated. "
102+
"Use 'basic_auth' or 'bearer_auth' parameters instead",
103+
category=DeprecationWarning,
104+
stacklevel=warn_stacklevel(),
105+
)
106+
81107
resolved_api_key = api_key if api_key is not DEFAULT else None
82108
resolved_bearer_auth = bearer_auth if bearer_auth is not DEFAULT else None
83109
if resolved_api_key or resolved_basic_auth or resolved_bearer_auth:

elasticsearch/_sync/client/utils.py

+35
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
# under the License.
1717

1818
import base64
19+
import inspect
1920
import urllib.parse
2021
import warnings
2122
from datetime import date, datetime
@@ -43,6 +44,7 @@
4344
AsyncTransport,
4445
HttpHeaders,
4546
NodeConfig,
47+
RequestsHttpNode,
4648
SniffOptions,
4749
Transport,
4850
)
@@ -90,6 +92,7 @@ class Stability(Enum):
9092

9193
_TRANSPORT_OPTIONS = {
9294
"api_key",
95+
"http_auth",
9396
"request_timeout",
9497
"opaque_id",
9598
"headers",
@@ -102,6 +105,7 @@ class Stability(Enum):
102105
def client_node_configs(
103106
hosts: Optional[_TYPE_HOSTS],
104107
cloud_id: Optional[str],
108+
requests_session_auth: Optional[Any] = None,
105109
**kwargs: Any,
106110
) -> List[NodeConfig]:
107111
if cloud_id is not None:
@@ -122,6 +126,12 @@ def client_node_configs(
122126
headers.setdefault("user-agent", USER_AGENT)
123127
node_options["headers"] = headers
124128

129+
# If a custom Requests AuthBase is passed we set that via '_extras'.
130+
if requests_session_auth is not None:
131+
node_options.setdefault("_extras", {})[
132+
"requests.session.auth"
133+
] = requests_session_auth
134+
125135
def apply_node_options(node_config: NodeConfig) -> NodeConfig:
126136
"""Needs special handling of headers since .replace() wipes out existing headers"""
127137
headers = node_config.headers.copy() # type: ignore[attr-defined]
@@ -438,3 +448,28 @@ def wrapped(*args: Any, **kwargs: Any) -> Any:
438448
return wrapped # type: ignore[return-value]
439449

440450
return wrapper
451+
452+
453+
def is_requests_http_auth(http_auth: Any) -> bool:
454+
"""Detect if an http_auth value is a custom Requests auth object"""
455+
try:
456+
from requests.auth import AuthBase
457+
458+
return isinstance(http_auth, AuthBase)
459+
except ImportError:
460+
pass
461+
return False
462+
463+
464+
def is_requests_node_class(node_class: Any) -> bool:
465+
"""Detect if 'RequestsHttpNode' would be used given the setting of 'node_class'"""
466+
return (
467+
node_class is not None
468+
and node_class is not DEFAULT
469+
and (
470+
node_class == "requests"
471+
or (
472+
inspect.isclass(node_class) and issubclass(node_class, RequestsHttpNode)
473+
)
474+
)
475+
)

elasticsearch/helpers/actions.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -698,12 +698,16 @@ def pop_transport_kwargs(kw: MutableMapping[str, Any]) -> Dict[str, Any]:
698698
for key in (
699699
"headers",
700700
"api_key",
701+
"http_auth",
701702
"basic_auth",
702703
"bearer_auth",
703704
"opaque_id",
704705
):
705706
try:
706-
transport_kwargs[key] = kw.pop(key)
707+
value = kw.pop(key)
708+
if key == "http_auth":
709+
key = "basic_auth"
710+
transport_kwargs[key] = value
707711
except KeyError:
708712
pass
709713
return transport_kwargs

test_elasticsearch/test_async/test_server/test_helpers.py

+4-2
Original file line numberDiff line numberDiff line change
@@ -741,8 +741,7 @@ async def test_clear_scroll(self, async_client, scan_teardown):
741741
"kwargs",
742742
[
743743
{"api_key": ("name", "value")},
744-
{"basic_auth": ("username", "password")},
745-
{"bearer_auth": "token"},
744+
{"http_auth": ("username", "password")},
746745
{"headers": {"custom", "header"}},
747746
],
748747
)
@@ -791,6 +790,9 @@ async def test_scan_auth_kwargs_forwarded(
791790

792791
assert data == [{"search_data": 1}]
793792

793+
if "http_auth" in kwargs:
794+
kwargs = {"basic_auth": kwargs.pop("http_auth")}
795+
794796
assert options.call_args_list == [
795797
call(request_timeout=None, **kwargs),
796798
call(ignore_status=404),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# Licensed to Elasticsearch B.V. under one or more contributor
2+
# license agreements. See the NOTICE file distributed with
3+
# this work for additional information regarding copyright
4+
# ownership. Elasticsearch B.V. licenses this file to you under
5+
# the Apache License, Version 2.0 (the "License"); you may
6+
# not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing,
12+
# software distributed under the License is distributed on an
13+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
# KIND, either express or implied. See the License for the
15+
# specific language governing permissions and limitations
16+
# under the License.
17+
18+
import warnings
19+
20+
import pytest
21+
22+
from elasticsearch import Elasticsearch
23+
24+
25+
def test_http_auth():
26+
with warnings.catch_warnings(record=True) as w:
27+
client = Elasticsearch(
28+
"http://localhost:9200", http_auth=("username", "password")
29+
)
30+
31+
assert len(w) == 1
32+
assert w[0].category == DeprecationWarning
33+
assert (
34+
str(w[0].message)
35+
== "The 'http_auth' parameter is deprecated. Use 'basic_auth' or 'bearer_auth' parameters instead"
36+
)
37+
assert client._headers["Authorization"] == "Basic dXNlcm5hbWU6cGFzc3dvcmQ="
38+
39+
with pytest.raises(ValueError) as e:
40+
Elasticsearch(
41+
"http://localhost:9200",
42+
http_auth=("username", "password"),
43+
basic_auth=("username", "password"),
44+
)
45+
assert (
46+
str(e.value)
47+
== "Can't specify both 'http_auth' and 'basic_auth', instead only specify 'basic_auth'"
48+
)

0 commit comments

Comments
 (0)