10000 feat: add possibility to authenticate by username/password (#435) · chaosegg/influxdb-client-python@5168a04 · GitHub
[go: up one dir, main page]

Skip to content

Commit 5168a04

Browse files
authored
feat: add possibility to authenticate by username/password (influxdata#435)
1 parent 60438b2 commit 5168a04

File tree

12 files changed

+196
-20
lines changed

12 files changed

+196
-20
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
## 1.29.0 [unreleased]
22

3+
### Features
4+
1. [#435](https://github.com/influxdata/influxdb-client-python/pull/435): Add possibility to authenticate by `username/password`
5+
36
### Breaking Changes
47
1. [#433](https://github.com/influxdata/influxdb-client-python/pull/433): Rename `InvocableScripts` to `InvokableScripts`
58

README.rst

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1089,6 +1089,64 @@ Gzip support
10891089
10901090
.. marker-gzip-end
10911091
1092+
Authenticate to the InfluxDB
1093+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1094+
.. marker-authenticate-start
1095+
1096+
``InfluxDBClient`` supports three options how to authorize a connection:
1097+
1098+
- `Token`
1099+
- `Username & Password`
1100+
- `HTTP Basic`
1101+
1102+
Token
1103+
"""""
1104+
1105+
Use the ``token`` to authenticate to the InfluxDB API. In your API requests, an `Authorization` header will be send.
1106+
The header value, provide the word `Token` followed by a space and an InfluxDB API token. The word `token`` is case-sensitive.
1107+
1108+
.. code-block:: python
1109+
1110+
from influxdb_client import InfluxDBClient
1111+
1112+
with InfluxDBClient(url="http://localhost:8086", token="my-token") as client
1113+
1114+
.. note:: Note that this is a preferred way how to authenticate to InfluxDB API.
1115+
1116+
Username & Password
1117+
"""""""""""""""""""
1118+
1119+
Authenticates via username and password credentials. If successful, creates a new session for the user.
1120+
1121+
.. code-block:: python
1122+
1123+
from influxdb_client import InfluxDBClient
1124+
1125+
with InfluxDBClient(url="http://localhost:8086", username="my-user", password="my-password") as client
1126+
1127+
.. warning::
1128+
1129+
The ``username/password`` auth is based on the HTTP "Basic" authentication.
1130+
The authorization expires when the `time-to-live (TTL) <https://docs.influxdata.com/influxdb/latest/reference/config-options/#session-length>`__
1131+
(default 60 minutes) is reached and client produces ``unauthorized exception``.
1132+
1133+
HTTP Basic
1134+
""""""""""
1135+
1136+
Use this to enable basic authentication when talking to a InfluxDB 1.8.x that does not use auth-enabled
1137+
but is protected by a reverse proxy with basic authentication.
1138+
1139+
.. code-block:: python
1140+
1141+
from influxdb_client import InfluxDBClient
1142+
1143+
with InfluxDBClient(url="http://localhost:8086", auth_basic=True, token="my-proxy-secret") as client
1144+
1145+
1146+
.. warning:: Don't use this when directly talking to InfluxDB 2.
1147+
1148+
.. marker-authenticate-end
1149+
10921150
Proxy configuration
10931151
^^^^^^^^^^^^^^^^^^^
10941152
.. marker-proxy-start

docs/usage.rst

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,23 @@ Write
1616
:start-after: marker-writes-start
1717
:end-before: marker-writes-end
1818

19+
Delete data
20+
^^^^^^^^^^^
21+
.. include:: ../README.rst
22+
:start-after: marker-delete-start
23+
:end-before: marker-delete-end
24+
1925
Pandas DataFrame
2026
^^^^^^^^^^^^^^^^
2127
.. include:: ../README.rst
2228
:start-after: marker-pandas-start
2329
:end-before: marker-pandas-end
2430

25-
Delete data
26-
^^^^^^^^^^^
31+
How to use Asyncio
32+
^^^^^^^^^^^^^^^^^^
2733
.. include:: ../README.rst
28-
:start-after: marker-delete-start
29-
:end-before: marker-delete-end
34+
:start-after: marker-asyncio-start
35+
:end-before: marker-asyncio-end
3036

3137
Gzip support
3238
^^^^^^^^^^^^
@@ -40,6 +46,12 @@ Proxy configuration
4046
:start-after: marker-proxy-start
4147
:end-before: marker-proxy-end
4248

49+
Authentication
50+
^^^^^^^^^^^^^^
51+
.. include:: ../README.rst
52+
:start-after: marker-authenticate-start
53+
:end-before: marker-authenticate-end
54+
4355
Nanosecond precision
4456
^^^^^^^^^^^^^^^^^^^^
4557
.. include:: ../README.rst
@@ -52,12 +64,6 @@ Handling Errors
5264
:start-after: marker-handling-errors-start
5365
:end-before: marker-handling-errors-end
5466

55-
How to use Asyncio
56-
^^^^^^^^^^^^^^^^^^
57-
.. include:: ../README.rst
58-
:start-after: marker-asyncio-start
59-
:end-before: marker-asyncio-end
60-
6167
Logging
6268
^^^^^^^
6369

influxdb_client/_async/api_client.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@
2525
from influxdb_client.configuration import Configuration
2626
import influxdb_client.domain
2727
from influxdb_client._async import rest
28+
from influxdb_client import SigninService
29+
from influxdb_client import SignoutService
30+
from influxdb_client.rest import _requires_create_user_session, _requires_expire_user_session
2831

2932

3033
class ApiClientAsync(object):
@@ -81,6 +84,7 @@ def __init__(self, configuration=None, header_name=None, header_value=None,
8184

8285
async def close(self):
8386
"""Dispose api client."""
87+
await self._signout()
8488
await self.rest_client.close()
8589
"""Dispose pools."""
8690
if self._pool:
@@ -117,6 +121,7 @@ async def __call_api(
117121
_preload_content=True, _request_timeout=None, urlopen_kw=None):
118122

119123
config = self.configuration
124+
await self._signin(resource_path=resource_path)
120125

121126
# header parameters
122127
header_params = header_params or {}
@@ -649,3 +654,13 @@ def __deserialize_model(self, data, klass):
649654
if klass_name:
650655
instance = self.__deserialize(data, klass_name)
651656
return instance
657+
658+
async def _signin(self, resource_path: str):
659+
if _requires_create_user_session(self.configuration, self.cookie, resource_path):
660+
http_info = await SigninService(self).post_signin_async()
661+
self.cookie = http_info[2]['set-cookie']
662+
663+
async def _signout(self):
664+
if _requires_expire_user_session(self.configuration, self.cookie):
665+
await SignoutService(self).post_signout_async()
666+
self.cookie = None

influxdb_client/_sync/api_client.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@
2525
from influxdb_client.configuration import Configuration
2626
import influxdb_client.domain
2727
from influxdb_client._sync import rest
28+
from influxdb_client import SigninService
29+
from influxdb_client import SignoutService
30+
from influxdb_client.rest import _requires_create_user_session, _requires_expire_user_session
2831

2932

3033
class ApiClient(object):
@@ -81,6 +84,7 @@ def __init__(self, configuration=None, header_name=None, header_value=None,
8184

8285
def __del__(self):
8386
"""Dispose pools."""
87+
self._signout()
8488
if self._pool:
8589
self._pool.close()
8690
self._pool.join()
@@ -117,6 +121,7 @@ def __call_api(
117121
_preload_content=True, _request_timeout=None, urlopen_kw=None):
118122

119123
config = self.configuration
124+
self._signin(resource_path=resource_path)
120125

121126
# header parameters
122127
header_params = header_params or {}
@@ -649,3 +654,13 @@ def __deserialize_model(self, data, klass):
649654
if klass_name:
650655
instance = self.__deserialize(data, klass_name)
651656
return instance
657+
658+
def _signin(self, resource_path: str):
659+
if _requires_create_user_session(self.configuration, self.cookie, resource_path):
660+
http_info = SigninService(self).post_signin_with_http_info()
661+
self.cookie = http_info[2]['set-cookie']
662+
663+
def _signout(self):
664+
if _requires_expire_user_session(self.configuration, self.cookie):
665+
SignoutService(self).post_signout()
666+
self.cookie = None

influxdb_client/client/_base.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -70,13 +70,20 @@ def __init__(self, url, token, debug=None, timeout=10_000, enable_gzip=False, or
7070
self.conf.loggers[client_logger] = logging.getLogger(client_logger)
7171
self.conf.debug = debug
7272

73-
auth_token = self.token
73+
self.conf.username = kwargs.get('username', None)
74+
self.conf.password = kwargs.get('password', None)
75+
# by token
7476
self.auth_header_name = "Authorization"
75-
self.auth_header_value = "Token " + auth_token
76-
77+
if self.token:
78+
self.auth_header_value = "Token " + self.token
79+
# by HTTP basic
7780
auth_basic = kwargs.get('auth_basic', False)
7881
if auth_basic:
7982
self.auth_header_value = "Basic " + base64.b64encode(token.encode()).decode()
83+
# by username, password
84+
if self.conf.username and self.conf.password:
85+
self.auth_header_name = None
86+
self.auth_header_value = None
8087

8188
self.retries = kwargs.get('retries', False)
8289

@@ -459,6 +466,8 @@ class _Configuration(Configuration):
459466
def __init__(self):
460467
Configuration.__init__(self)
461468
self.enable_gzip = False
469+
self.username = None
470+
self.password = None
462471

463472
def update_request_header_params(self, path: str, params: dict):
464473
super().update_request_header_params(path, params)

influxdb_client/client/influxdb_client.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,13 @@
2424
class InfluxDBClient(_BaseClient):
2525
"""InfluxDBClient is client for InfluxDB v2."""
2626

27-
def __init__(self, url, token, debug=None, timeout=10_000, enable_gzip=False, org: str = None,
27+
def __init__(self, url, token: str = None, debug=None, timeout=10_000, enable_gzip=False, org: str = None,
2828
default_tags: dict = None, **kwargs) -> None:
2929
"""
3030
Initialize defaults.
3131
3232
:param url: InfluxDB server API url (ex. http://localhost:8086).
33-
:param token: auth token
33+
:param token: ``token`` to authenticate to the InfluxDB API
3434
:param debug: enable verbose logging of http requests
3535
:param timeout: HTTP client timeout setting for a request specified in milliseconds.
3636
If one number provided, it will be total request timeout.
@@ -50,6 +50,8 @@ def __init__(self, url, token, debug=None, timeout=10_000, enable_gzip=False, or
5050
:key bool auth_basic: Set this to true to enable basic authentication when talking to a InfluxDB 1.8.x that
5151
does not use auth-enabled but is protected by a reverse proxy with basic authentication.
5252
(defaults to false, don't set to true when talking to InfluxDB 2)
53+
:key str username: ``username`` to authenticate via username and password credentials to the InfluxDB 2.x
54+
:key str password: ``password`` to authenticate via username and password credentials to the InfluxDB 2.x
5355
:key list[str] profilers: list of enabled Flux profilers
5456
"""
5557
super().__init__(url=url, token=token, debug=debug, timeout=timeout, enable_gzip=enable_gzip, org=org,

influxdb_client/client/influxdb_client_async.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,13 @@
1616
class InfluxDBClientAsync(_BaseClient):
1717
"""InfluxDBClientAsync is client for InfluxDB v2."""
1818

19-
def __init__(self, url, token, org: str = None, debug=None, timeout=10_000, enable_gzip=False, **kwargs) -> None:
19+
def __init__(self, url, token: str = None, org: str = None, debug=None, timeout=10_000, enable_gzip=False,
20+
**kwargs) -> None:
2021
"""
2122
Initialize defaults.
2223
2324
:param url: InfluxDB server API url (ex. http://localhost:8086).
24-
:param token: auth token
25+
:param token: ``token`` to authenticate to the InfluxDB 2.x
2526
:param org: organization name (used as a default in Query, Write and Delete API)
2627
:param debug: enable verbose logging of http requests
2728
:param timeout: The maximal number of milliseconds for the whole HTTP request including
@@ -39,6 +40,8 @@ def __init__(self, url, token, org: str = None, debug=None, timeout=10_000, enab
3940
:key bool auth_basic: Set this to true to enable basic authentication when talking to a InfluxDB 1.8.x that
4041
does not use auth-enabled but is protected by a reverse proxy with basic authentication.
4142
(defaults to false, don't set to true when talking to InfluxDB 2)
43+
:key str username: ``username`` to authenticate via username and password credentials to the InfluxDB 2.x
44+
:key str password: ``password`` to authenticate via username and password credentials to the InfluxDB 2.x
4245
:key bool allow_redirects: If set to ``False``, do not follow HTTP redirects. ``True`` by default.
4346
:key int max_redirects: Maximum number of HTTP redirects to follow. ``10`` by default.
4447
:key dict client_session_kwargs: Additional configuration arguments for :class:`~aiohttp.ClientSession`

influxdb_client/rest.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@
99
Generated by: https://openapi-generator.tech
1010
"""
1111

12-
1312
from __future__ import absolute_import
1413

1514
from influxdb_client.client.exceptions import InfluxDBError
15+
from influxdb_client.configuration import Configuration
1616

171 10000 7
_UTF_8_encoding = 'utf-8'
1818

@@ -40,7 +40,7 @@ def __init__(self, status=None, reason=None, http_resp=None):
4040

4141
def __str__(self):
4242
"""Get custom error messages for exception."""
43-
error_message = "({0})\n"\
43+
error_message = "({0})\n" \
4444
"Reason: {1}\n".format(self.status, self.reason)
4545
if self.headers:
4646
error_message += "HTTP response headers: {0}\n".format(
@@ -50,3 +50,12 @@ def __str__(self):
5050
error_message += "HTTP response body: {0}\n".format(self.body)
5151

5252
return error_message
53+
54+
55+
def _requires_create_user_session(configuration: Configuration, cookie: str, resource_path: str):
56+
_unauthorized = ['/api/v2/signin', '/api/v2/signout']
57+
return configuration.username and configuration.password and not cookie and resource_path not in _unauthorized
58+
59+
60+
def _requires_expire_user_session(configuration: Configuration, cookie: str):
61+
return configuration.username and configuration.password and cookie

tests/test_InfluxDBClient.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,11 +216,16 @@ def test_version(self):
216216

217217
def test_version_not_running_instance(self):
218218
client_not_running = InfluxDBClient("http://localhost:8099", token="my-token", debug=True)
219-
with self.assertRaises(NewConnectionError) as cm:
219+
with self.assertRaises(NewConnectionError):
220220
client_not_running.version()
221221

222222
client_not_running.close()
223223

224+
def test_username_password_authorization(self):
225+
self.client.close()
226+
self.client = InfluxDBClient(url=self.host, username="my-user", password="my-password", debug=True)
227+
self.client.query_api().query("buckets()", "my-org")
228+
224229
def _start_proxy_server(self):
225230
import http.server
226231
import urllib.request

0 commit comments

Comments
 (0)
0