snowflake-connector-python 3.16.0__cp313-cp313-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- snowflake/connector/__init__.py +96 -0
- snowflake/connector/_query_context_cache.py +276 -0
- snowflake/connector/_sql_util.py +48 -0
- snowflake/connector/_utils.py +70 -0
- snowflake/connector/arrow_context.py +203 -0
- snowflake/connector/auth/__init__.py +53 -0
- snowflake/connector/auth/_auth.py +586 -0
- snowflake/connector/auth/_http_server.py +220 -0
- snowflake/connector/auth/_oauth_base.py +400 -0
- snowflake/connector/auth/by_plugin.py +219 -0
- snowflake/connector/auth/default.py +36 -0
- snowflake/connector/auth/idtoken.py +72 -0
- snowflake/connector/auth/keypair.py +222 -0
- snowflake/connector/auth/no_auth.py +39 -0
- snowflake/connector/auth/oauth.py +49 -0
- snowflake/connector/auth/oauth_code.py +479 -0
- snowflake/connector/auth/oauth_credentials.py +66 -0
- snowflake/connector/auth/okta.py +335 -0
- snowflake/connector/auth/pat.py +39 -0
- snowflake/connector/auth/usrpwdmfa.py +69 -0
- snowflake/connector/auth/webbrowser.py +502 -0
- snowflake/connector/auth/workload_identity.py +94 -0
- snowflake/connector/azure_storage_client.py +276 -0
- snowflake/connector/backoff_policies.py +141 -0
- snowflake/connector/bind_upload_agent.py +89 -0
- snowflake/connector/cache.py +696 -0
- snowflake/connector/compat.py +127 -0
- snowflake/connector/config_manager.py +496 -0
- snowflake/connector/connection.py +2273 -0
- snowflake/connector/connection_diagnostic.py +776 -0
- snowflake/connector/constants.py +442 -0
- snowflake/connector/converter.py +785 -0
- snowflake/connector/converter_issue23517.py +87 -0
- snowflake/connector/converter_null.py +14 -0
- snowflake/connector/converter_snowsql.py +205 -0
- snowflake/connector/cursor.py +1951 -0
- snowflake/connector/dbapi.py +53 -0
- snowflake/connector/description.py +19 -0
- snowflake/connector/direct_file_operation_utils.py +88 -0
- snowflake/connector/encryption_util.py +220 -0
- snowflake/connector/errorcode.py +91 -0
- snowflake/connector/errors.py +615 -0
- snowflake/connector/externals_utils/__init__.py +0 -0
- snowflake/connector/externals_utils/externals_setup.py +27 -0
- snowflake/connector/feature.py +4 -0
- snowflake/connector/file_compression_type.py +118 -0
- snowflake/connector/file_lock.py +72 -0
- snowflake/connector/file_transfer_agent.py +1215 -0
- snowflake/connector/file_util.py +153 -0
- snowflake/connector/gcs_storage_client.py +474 -0
- snowflake/connector/gzip_decoder.py +85 -0
- snowflake/connector/local_storage_client.py +90 -0
- snowflake/connector/log_configuration.py +60 -0
- snowflake/connector/logging_utils/__init__.py +0 -0
- snowflake/connector/logging_utils/filters.py +72 -0
- snowflake/connector/nanoarrow_arrow_iterator.cp313-win_amd64.pyd +0 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/ArrayConverter.cpp +60 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/ArrayConverter.hpp +29 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/BinaryConverter.cpp +19 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/BinaryConverter.hpp +26 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/BooleanConverter.cpp +21 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/BooleanConverter.hpp +23 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/CArrowChunkIterator.cpp +557 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/CArrowChunkIterator.hpp +98 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/CArrowIterator.cpp +125 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/CArrowIterator.hpp +115 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/CArrowTableIterator.cpp +1005 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/CArrowTableIterator.hpp +127 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/DateConverter.cpp +47 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/DateConverter.hpp +46 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/DecFloatConverter.cpp +83 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/DecFloatConverter.hpp +35 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/DecimalConverter.cpp +97 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/DecimalConverter.hpp +72 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/FixedSizeListConverter.cpp +73 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/FixedSizeListConverter.hpp +28 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/FloatConverter.cpp +30 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/FloatConverter.hpp +35 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/IColumnConverter.hpp +17 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/IntConverter.cpp +23 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/IntConverter.hpp +45 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/IntervalConverter.cpp +71 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/IntervalConverter.hpp +56 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/LICENSE.txt +209 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/MapConverter.cpp +75 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/MapConverter.hpp +30 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/ObjectConverter.cpp +46 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/ObjectConverter.hpp +29 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/Python/Common.cpp +8 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/Python/Common.hpp +95 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/Python/Helpers.cpp +57 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/Python/Helpers.hpp +36 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/SnowflakeType.cpp +34 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/SnowflakeType.hpp +48 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/StringConverter.cpp +19 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/StringConverter.hpp +26 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/TimeConverter.cpp +36 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/TimeConverter.hpp +31 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/TimeStampConverter.cpp +346 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/TimeStampConverter.hpp +145 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/Util/macros.hpp +14 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/Util/time.cpp +65 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/Util/time.hpp +68 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_accessors.h +101 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_alloc.h +127 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_assert.h +45 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_builder.h +1908 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_emitter.h +215 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_endian.h +125 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_epilogue.h +7 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_flatbuffers.h +55 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_identifier.h +148 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_iov.h +31 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_prologue.h +8 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_refmap.h +144 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_rtconfig.h +162 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_types.h +97 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_verifier.h +239 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/flatcc_portable.h +14 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/paligned_alloc.h +210 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pattributes.h +84 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pdiagnostic.h +84 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pdiagnostic_pop.h +20 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pdiagnostic_push.h +51 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pendian.h +206 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pendian_detect.h +118 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pinline.h +19 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pinttypes.h +52 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/portable.h +2 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/portable_basic.h +25 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pstatic_assert.h +67 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pstdalign.h +162 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pstdint.h +898 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/punaligned.h +190 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pversion.h +6 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pwarnings.h +52 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc.c +3204 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow.c +3217 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow.h +3618 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow.hpp +379 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow_arrow_iterator.pyx +256 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow_device.c +512 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow_device.h +350 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow_ipc.c +33273 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow_ipc.h +397 -0
- snowflake/connector/nanoarrow_cpp/Logging/logging.cpp +100 -0
- snowflake/connector/nanoarrow_cpp/Logging/logging.hpp +49 -0
- snowflake/connector/network.py +1297 -0
- snowflake/connector/ocsp_asn1crypto.py +447 -0
- snowflake/connector/ocsp_snowflake.py +1883 -0
- snowflake/connector/options.py +131 -0
- snowflake/connector/pandas_tools.py +732 -0
- snowflake/connector/proxy.py +43 -0
- snowflake/connector/py.typed +0 -0
- snowflake/connector/result_batch.py +786 -0
- snowflake/connector/result_set.py +319 -0
- snowflake/connector/s3_storage_client.py +605 -0
- snowflake/connector/secret_detector.py +181 -0
- snowflake/connector/sf_dirs.py +64 -0
- snowflake/connector/sfbinaryformat.py +35 -0
- snowflake/connector/sfdatetime.py +345 -0
- snowflake/connector/snow_logging.py +144 -0
- snowflake/connector/sqlstate.py +9 -0
- snowflake/connector/ssd_internal_keys.py +30 -0
- snowflake/connector/ssl_wrap_socket.py +134 -0
- snowflake/connector/storage_client.py +478 -0
- snowflake/connector/telemetry.py +249 -0
- snowflake/connector/telemetry_oob.py +543 -0
- snowflake/connector/test_util.py +30 -0
- snowflake/connector/time_util.py +159 -0
- snowflake/connector/token_cache.py +402 -0
- snowflake/connector/tool/__init__.py +0 -0
- snowflake/connector/tool/dump_certs.py +57 -0
- snowflake/connector/tool/dump_ocsp_response.py +139 -0
- snowflake/connector/tool/dump_ocsp_response_cache.py +194 -0
- snowflake/connector/tool/probe_connection.py +69 -0
- snowflake/connector/url_util.py +49 -0
- snowflake/connector/util_text.py +301 -0
- snowflake/connector/vendored/__init__.py +3 -0
- snowflake/connector/vendored/requests/LICENSE +175 -0
- snowflake/connector/vendored/requests/__init__.py +169 -0
- snowflake/connector/vendored/requests/__version__.py +14 -0
- snowflake/connector/vendored/requests/_internal_utils.py +50 -0
- snowflake/connector/vendored/requests/adapters.py +537 -0
- snowflake/connector/vendored/requests/api.py +157 -0
- snowflake/connector/vendored/requests/auth.py +315 -0
- snowflake/connector/vendored/requests/certs.py +17 -0
- snowflake/connector/vendored/requests/compat.py +79 -0
- snowflake/connector/vendored/requests/cookies.py +561 -0
- snowflake/connector/vendored/requests/exceptions.py +140 -0
- snowflake/connector/vendored/requests/help.py +134 -0
- snowflake/connector/vendored/requests/hooks.py +33 -0
- snowflake/connector/vendored/requests/models.py +1033 -0
- snowflake/connector/vendored/requests/sessions.py +833 -0
- snowflake/connector/vendored/requests/status_codes.py +128 -0
- snowflake/connector/vendored/requests/structures.py +99 -0
- snowflake/connector/vendored/requests/utils.py +1093 -0
- snowflake/connector/vendored/urllib3/LICENSE.txt +21 -0
- snowflake/connector/vendored/urllib3/__init__.py +85 -0
- snowflake/connector/vendored/urllib3/_collections.py +355 -0
- snowflake/connector/vendored/urllib3/_version.py +2 -0
- snowflake/connector/vendored/urllib3/connection.py +572 -0
- snowflake/connector/vendored/urllib3/connectionpool.py +1137 -0
- snowflake/connector/vendored/urllib3/contrib/__init__.py +0 -0
- snowflake/connector/vendored/urllib3/contrib/_appengine_environ.py +36 -0
- snowflake/connector/vendored/urllib3/contrib/_securetransport/__init__.py +0 -0
- snowflake/connector/vendored/urllib3/contrib/_securetransport/bindings.py +519 -0
- snowflake/connector/vendored/urllib3/contrib/_securetransport/low_level.py +397 -0
- snowflake/connector/vendored/urllib3/contrib/appengine.py +314 -0
- snowflake/connector/vendored/urllib3/contrib/ntlmpool.py +130 -0
- snowflake/connector/vendored/urllib3/contrib/pyopenssl.py +509 -0
- snowflake/connector/vendored/urllib3/contrib/securetransport.py +920 -0
- snowflake/connector/vendored/urllib3/contrib/socks.py +216 -0
- snowflake/connector/vendored/urllib3/exceptions.py +323 -0
- snowflake/connector/vendored/urllib3/fields.py +274 -0
- snowflake/connector/vendored/urllib3/filepost.py +98 -0
- snowflake/connector/vendored/urllib3/packages/__init__.py +0 -0
- snowflake/connector/vendored/urllib3/packages/backports/__init__.py +0 -0
- snowflake/connector/vendored/urllib3/packages/backports/makefile.py +51 -0
- snowflake/connector/vendored/urllib3/packages/backports/weakref_finalize.py +155 -0
- snowflake/connector/vendored/urllib3/packages/six.py +1076 -0
- snowflake/connector/vendored/urllib3/poolmanager.py +540 -0
- snowflake/connector/vendored/urllib3/request.py +191 -0
- snowflake/connector/vendored/urllib3/response.py +885 -0
- snowflake/connector/vendored/urllib3/util/__init__.py +49 -0
- snowflake/connector/vendored/urllib3/util/connection.py +156 -0
- snowflake/connector/vendored/urllib3/util/proxy.py +57 -0
- snowflake/connector/vendored/urllib3/util/queue.py +22 -0
- snowflake/connector/vendored/urllib3/util/request.py +146 -0
- snowflake/connector/vendored/urllib3/util/response.py +107 -0
- snowflake/connector/vendored/urllib3/util/retry.py +620 -0
- snowflake/connector/vendored/urllib3/util/ssl_.py +495 -0
- snowflake/connector/vendored/urllib3/util/ssl_match_hostname.py +159 -0
- snowflake/connector/vendored/urllib3/util/ssltransport.py +221 -0
- snowflake/connector/vendored/urllib3/util/timeout.py +271 -0
- snowflake/connector/vendored/urllib3/util/url.py +435 -0
- snowflake/connector/vendored/urllib3/util/wait.py +152 -0
- snowflake/connector/version.py +3 -0
- snowflake/connector/wif_util.py +407 -0
- snowflake_connector_python-3.16.0.dist-info/METADATA +1475 -0
- snowflake_connector_python-3.16.0.dist-info/RECORD +246 -0
- snowflake_connector_python-3.16.0.dist-info/WHEEL +5 -0
- snowflake_connector_python-3.16.0.dist-info/entry_points.txt +4 -0
- snowflake_connector_python-3.16.0.dist-info/licenses/LICENSE.txt +202 -0
- snowflake_connector_python-3.16.0.dist-info/licenses/NOTICE +8 -0
- snowflake_connector_python-3.16.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
"""This module implements the base class for authenticator classes.
|
|
5
|
+
|
|
6
|
+
Note:
|
|
7
|
+
**kwargs are added to most functions so that child classes can safely ignore extra in
|
|
8
|
+
arguments in case of a caller API change and named arguments are enforced to prevent
|
|
9
|
+
issues with argument being sent in out of order.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import logging
|
|
13
|
+
import time
|
|
14
|
+
from abc import ABC, abstractmethod
|
|
15
|
+
from enum import Enum, unique
|
|
16
|
+
from os import getenv
|
|
17
|
+
from typing import TYPE_CHECKING, Any, Iterator
|
|
18
|
+
|
|
19
|
+
from ..errorcode import ER_FAILED_TO_CONNECT_TO_DB
|
|
20
|
+
from ..errors import DatabaseError, Error, OperationalError
|
|
21
|
+
from ..sqlstate import SQLSTATE_CONNECTION_WAS_NOT_ESTABLISHED
|
|
22
|
+
from ..time_util import TimeoutBackoffCtx
|
|
23
|
+
|
|
24
|
+
if TYPE_CHECKING:
|
|
25
|
+
from .. import SnowflakeConnection
|
|
26
|
+
|
|
27
|
+
logger = logging.getLogger(__name__)
|
|
28
|
+
|
|
29
|
+
"""
|
|
30
|
+
Default value for max retry is 1 because
|
|
31
|
+
Python requests module already tries twice
|
|
32
|
+
by default. Unlike JWT where we need to refresh
|
|
33
|
+
token every 10 seconds, general authenticators
|
|
34
|
+
wait for 60 seconds before connection timeout
|
|
35
|
+
per attempt totaling a 240 sec wait time for a non
|
|
36
|
+
JWT based authenticator which is more than enough.
|
|
37
|
+
This can be changed ofcourse using MAX_CNXN_RETRY_ATTEMPTS
|
|
38
|
+
env variable.
|
|
39
|
+
"""
|
|
40
|
+
DEFAULT_MAX_CON_RETRY_ATTEMPTS = 1
|
|
41
|
+
DEFAULT_AUTH_CLASS_TIMEOUT = 120
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@unique
|
|
45
|
+
class AuthType(Enum):
|
|
46
|
+
DEFAULT = "SNOWFLAKE" # default authenticator name
|
|
47
|
+
EXTERNAL_BROWSER = "EXTERNALBROWSER"
|
|
48
|
+
KEY_PAIR = "SNOWFLAKE_JWT"
|
|
49
|
+
OAUTH = "OAUTH"
|
|
50
|
+
ID_TOKEN = "ID_TOKEN"
|
|
51
|
+
USR_PWD_MFA = "USERNAME_PASSWORD_MFA"
|
|
52
|
+
OKTA = "OKTA"
|
|
53
|
+
PAT = "PROGRAMMATIC_ACCESS_TOKEN'"
|
|
54
|
+
NO_AUTH = "NO_AUTH"
|
|
55
|
+
WORKLOAD_IDENTITY = "WORKLOAD_IDENTITY"
|
|
56
|
+
PAT_WITH_EXTERNAL_SESSION = "PAT_WITH_EXTERNAL_SESSION"
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class AuthByPlugin(ABC):
|
|
60
|
+
"""External Authenticator interface."""
|
|
61
|
+
|
|
62
|
+
def __init__(
|
|
63
|
+
self,
|
|
64
|
+
timeout: int | None = None,
|
|
65
|
+
backoff_generator: Iterator | None = None,
|
|
66
|
+
**kwargs,
|
|
67
|
+
) -> None:
|
|
68
|
+
self.consent_cache_id_token = False
|
|
69
|
+
|
|
70
|
+
self._retry_ctx = TimeoutBackoffCtx(
|
|
71
|
+
timeout=timeout if timeout is not None else DEFAULT_AUTH_CLASS_TIMEOUT,
|
|
72
|
+
max_retry_attempts=kwargs.get(
|
|
73
|
+
"max_retry_attempts",
|
|
74
|
+
int(getenv("MAX_CON_RETRY_ATTEMPTS", DEFAULT_MAX_CON_RETRY_ATTEMPTS)),
|
|
75
|
+
),
|
|
76
|
+
backoff_generator=backoff_generator,
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
# some authenticators may want to override socket level timeout
|
|
80
|
+
# for example, AuthByKeyPair will set this to ensure JWT tokens are refreshed in time
|
|
81
|
+
# if not None, this will override socket_timeout specified in connection
|
|
82
|
+
self._socket_timeout = None
|
|
83
|
+
|
|
84
|
+
@property
|
|
85
|
+
def timeout(self) -> int:
|
|
86
|
+
"""The timeout of _retry_ctx is guaranteed not to be None during AuthByPlugin initialization"""
|
|
87
|
+
return self._retry_ctx.timeout
|
|
88
|
+
|
|
89
|
+
@timeout.setter
|
|
90
|
+
def timeout(self) -> None:
|
|
91
|
+
logger.warning(
|
|
92
|
+
"Attempting to mutate timeout of AuthByPlugin. Create a new instance with desired parameters instead."
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
@property
|
|
96
|
+
@abstractmethod
|
|
97
|
+
def type_(self) -> AuthType:
|
|
98
|
+
"""Return the Snowflake friendly name of auth class."""
|
|
99
|
+
raise NotImplementedError
|
|
100
|
+
|
|
101
|
+
@property
|
|
102
|
+
@abstractmethod
|
|
103
|
+
def assertion_content(self) -> str:
|
|
104
|
+
"""Return a safe version of the information used to authenticate with Snowflake.
|
|
105
|
+
|
|
106
|
+
This is used for logging, useful for printing temporary tokens, but make sure to
|
|
107
|
+
mask secrets.
|
|
108
|
+
"""
|
|
109
|
+
raise NotImplementedError
|
|
110
|
+
|
|
111
|
+
@abstractmethod
|
|
112
|
+
def prepare(
|
|
113
|
+
self,
|
|
114
|
+
*,
|
|
115
|
+
conn: SnowflakeConnection,
|
|
116
|
+
authenticator: str,
|
|
117
|
+
service_name: str | None,
|
|
118
|
+
account: str,
|
|
119
|
+
user: str,
|
|
120
|
+
password: str | None,
|
|
121
|
+
**kwargs: Any,
|
|
122
|
+
) -> str | None:
|
|
123
|
+
"""Prepare for authentication.
|
|
124
|
+
|
|
125
|
+
This function is useful for situations where we need to reach out to a 3rd-party
|
|
126
|
+
service before authenticating with Snowflake.
|
|
127
|
+
"""
|
|
128
|
+
raise NotImplementedError
|
|
129
|
+
|
|
130
|
+
@abstractmethod
|
|
131
|
+
def update_body(self, body: dict[Any, Any]) -> None:
|
|
132
|
+
"""Update the body of the authentication request."""
|
|
133
|
+
raise NotImplementedError
|
|
134
|
+
|
|
135
|
+
@abstractmethod
|
|
136
|
+
def reset_secrets(self) -> None:
|
|
137
|
+
"""Reset secret members."""
|
|
138
|
+
raise NotImplementedError
|
|
139
|
+
|
|
140
|
+
@abstractmethod
|
|
141
|
+
def reauthenticate(
|
|
142
|
+
self,
|
|
143
|
+
*,
|
|
144
|
+
conn: SnowflakeConnection,
|
|
145
|
+
**kwargs: Any,
|
|
146
|
+
) -> dict[str, Any]:
|
|
147
|
+
"""Re-perform authentication.
|
|
148
|
+
|
|
149
|
+
The difference between this and authentication is that secrets will be removed
|
|
150
|
+
from memory by the time this gets called.
|
|
151
|
+
"""
|
|
152
|
+
raise NotImplementedError
|
|
153
|
+
|
|
154
|
+
def _handle_failure(
|
|
155
|
+
self,
|
|
156
|
+
*,
|
|
157
|
+
conn: SnowflakeConnection,
|
|
158
|
+
ret: dict[Any, Any],
|
|
159
|
+
**kwargs: Any,
|
|
160
|
+
) -> None:
|
|
161
|
+
"""Handles a failure when an issue happens while connecting to Snowflake.
|
|
162
|
+
|
|
163
|
+
If the user returns from this function execution will continue. The argument
|
|
164
|
+
data can be manipulated from within this function and so recovery is possible
|
|
165
|
+
from here.
|
|
166
|
+
"""
|
|
167
|
+
Error.errorhandler_wrapper(
|
|
168
|
+
conn,
|
|
169
|
+
None,
|
|
170
|
+
DatabaseError,
|
|
171
|
+
{
|
|
172
|
+
"msg": "Failed to connect to DB: {host}:{port}, {message}".format(
|
|
173
|
+
host=conn._rest._host,
|
|
174
|
+
port=conn._rest._port,
|
|
175
|
+
message=ret["message"],
|
|
176
|
+
),
|
|
177
|
+
"errno": int(ret.get("code", -1)),
|
|
178
|
+
"sqlstate": SQLSTATE_CONNECTION_WAS_NOT_ESTABLISHED,
|
|
179
|
+
},
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
def handle_timeout(
|
|
183
|
+
self,
|
|
184
|
+
*,
|
|
185
|
+
authenticator: str,
|
|
186
|
+
service_name: str | None,
|
|
187
|
+
account: str,
|
|
188
|
+
user: str,
|
|
189
|
+
password: str,
|
|
190
|
+
**kwargs: Any,
|
|
191
|
+
) -> None:
|
|
192
|
+
"""Default timeout handler.
|
|
193
|
+
|
|
194
|
+
This will trigger if the authenticator
|
|
195
|
+
hasn't implemented one. By default we retry on timeouts and use
|
|
196
|
+
jitter to deduce the time to sleep before retrying. The sleep
|
|
197
|
+
time ranges between 1 and 16 seconds.
|
|
198
|
+
"""
|
|
199
|
+
|
|
200
|
+
# Some authenticators may not want to delete the parameters to this function
|
|
201
|
+
# Currently, the only authenticator where this is the case is AuthByKeyPair
|
|
202
|
+
if kwargs.pop("delete_params", True):
|
|
203
|
+
del authenticator, service_name, account, user, password
|
|
204
|
+
|
|
205
|
+
logger.debug("Default timeout handler invoked for authenticator")
|
|
206
|
+
if not self._retry_ctx.should_retry:
|
|
207
|
+
error = OperationalError(
|
|
208
|
+
msg=f"Could not connect to Snowflake backend after {self._retry_ctx.current_retry_count + 1} attempt(s)."
|
|
209
|
+
"Aborting",
|
|
210
|
+
errno=ER_FAILED_TO_CONNECT_TO_DB,
|
|
211
|
+
)
|
|
212
|
+
raise error
|
|
213
|
+
else:
|
|
214
|
+
logger.debug(
|
|
215
|
+
f"Hit connection timeout, attempt number {self._retry_ctx.current_retry_count + 1}."
|
|
216
|
+
" Will retry in a bit..."
|
|
217
|
+
)
|
|
218
|
+
time.sleep(float(self._retry_ctx.current_sleep_time))
|
|
219
|
+
self._retry_ctx.increment()
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from .by_plugin import AuthByPlugin, AuthType
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class AuthByDefault(AuthByPlugin):
|
|
10
|
+
"""Default username and password authenticator."""
|
|
11
|
+
|
|
12
|
+
@property
|
|
13
|
+
def type_(self) -> AuthType:
|
|
14
|
+
return AuthType.DEFAULT
|
|
15
|
+
|
|
16
|
+
@property
|
|
17
|
+
def assertion_content(self) -> str:
|
|
18
|
+
return "*********"
|
|
19
|
+
|
|
20
|
+
def __init__(self, password: str, **kwargs) -> None:
|
|
21
|
+
"""Initializes an instance with a password."""
|
|
22
|
+
super().__init__(**kwargs)
|
|
23
|
+
self._password: str | None = password
|
|
24
|
+
|
|
25
|
+
def reset_secrets(self) -> None:
|
|
26
|
+
self._password = None
|
|
27
|
+
|
|
28
|
+
def prepare(self, **kwargs: Any) -> None:
|
|
29
|
+
pass
|
|
30
|
+
|
|
31
|
+
def reauthenticate(self, **kwargs: Any) -> dict[str, bool]:
|
|
32
|
+
return {"success": False}
|
|
33
|
+
|
|
34
|
+
def update_body(self, body: dict[Any, Any]) -> None:
|
|
35
|
+
"""Sets the password if available."""
|
|
36
|
+
body["data"]["PASSWORD"] = self._password
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from typing import TYPE_CHECKING, Any
|
|
5
|
+
|
|
6
|
+
from ..network import ID_TOKEN_AUTHENTICATOR
|
|
7
|
+
from .by_plugin import AuthByPlugin, AuthType
|
|
8
|
+
from .webbrowser import AuthByWebBrowser
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from ..connection import SnowflakeConnection
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class AuthByIdToken(AuthByPlugin):
|
|
15
|
+
"""Internal IdToken Based Authentication.
|
|
16
|
+
|
|
17
|
+
Works by accepting an id_toke and use that to authenticate. Only be used when users are using EXTERNAL_BROWSER_AUTHENTICATOR
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
@property
|
|
21
|
+
def type_(self) -> AuthType:
|
|
22
|
+
return AuthType.ID_TOKEN
|
|
23
|
+
|
|
24
|
+
@property
|
|
25
|
+
def assertion_content(self) -> str:
|
|
26
|
+
return self._id_token
|
|
27
|
+
|
|
28
|
+
def __init__(
|
|
29
|
+
self,
|
|
30
|
+
id_token: str,
|
|
31
|
+
application: str,
|
|
32
|
+
protocol: str | None,
|
|
33
|
+
host: str | None,
|
|
34
|
+
port: str | None,
|
|
35
|
+
**kwargs,
|
|
36
|
+
) -> None:
|
|
37
|
+
"""Initialized an instance with an IdToken."""
|
|
38
|
+
super().__init__(**kwargs)
|
|
39
|
+
self._id_token: str | None = id_token
|
|
40
|
+
self._application = application
|
|
41
|
+
self._protocol = protocol
|
|
42
|
+
self._host = host
|
|
43
|
+
self._port = port
|
|
44
|
+
|
|
45
|
+
def reset_secrets(self) -> None:
|
|
46
|
+
self._id_token = None
|
|
47
|
+
|
|
48
|
+
def prepare(self, **kwargs: Any) -> None:
|
|
49
|
+
pass
|
|
50
|
+
|
|
51
|
+
def reauthenticate(
|
|
52
|
+
self,
|
|
53
|
+
*,
|
|
54
|
+
conn: SnowflakeConnection,
|
|
55
|
+
**kwargs: Any,
|
|
56
|
+
) -> dict[str, bool]:
|
|
57
|
+
conn.auth_class = AuthByWebBrowser(
|
|
58
|
+
application=self._application,
|
|
59
|
+
protocol=self._protocol,
|
|
60
|
+
host=self._host,
|
|
61
|
+
port=self._port,
|
|
62
|
+
timeout=conn.login_timeout,
|
|
63
|
+
backoff_generator=conn._backoff_generator,
|
|
64
|
+
)
|
|
65
|
+
conn._authenticate(conn.auth_class)
|
|
66
|
+
conn._auth_class.reset_secrets()
|
|
67
|
+
return {"success": True}
|
|
68
|
+
|
|
69
|
+
def update_body(self, body: dict[Any, Any]) -> None:
|
|
70
|
+
"""Idtoken needs the authenticator and token attributes set."""
|
|
71
|
+
body["data"]["AUTHENTICATOR"] = ID_TOKEN_AUTHENTICATOR
|
|
72
|
+
body["data"]["TOKEN"] = self._id_token
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import base64
|
|
5
|
+
import hashlib
|
|
6
|
+
import os
|
|
7
|
+
from datetime import datetime, timedelta, timezone
|
|
8
|
+
from logging import getLogger
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
import jwt
|
|
12
|
+
from cryptography.hazmat.backends import default_backend
|
|
13
|
+
from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateKey
|
|
14
|
+
from cryptography.hazmat.primitives.serialization import (
|
|
15
|
+
Encoding,
|
|
16
|
+
PublicFormat,
|
|
17
|
+
load_der_private_key,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
from ..errorcode import ER_CONNECTION_TIMEOUT, ER_INVALID_PRIVATE_KEY
|
|
21
|
+
from ..errors import OperationalError, ProgrammingError
|
|
22
|
+
from ..network import KEY_PAIR_AUTHENTICATOR
|
|
23
|
+
from .by_plugin import AuthByPlugin, AuthType
|
|
24
|
+
|
|
25
|
+
logger = getLogger(__name__)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class AuthByKeyPair(AuthByPlugin):
|
|
29
|
+
"""Key pair based authentication."""
|
|
30
|
+
|
|
31
|
+
ALGORITHM = "RS256"
|
|
32
|
+
ISSUER = "iss"
|
|
33
|
+
SUBJECT = "sub"
|
|
34
|
+
EXPIRE_TIME = "exp"
|
|
35
|
+
ISSUE_TIME = "iat"
|
|
36
|
+
LIFETIME = 60
|
|
37
|
+
DEFAULT_JWT_RETRY_ATTEMPTS = 10
|
|
38
|
+
DEFAULT_JWT_CNXN_WAIT_TIME = 10
|
|
39
|
+
|
|
40
|
+
def __init__(
|
|
41
|
+
self,
|
|
42
|
+
private_key: bytes | str | RSAPrivateKey,
|
|
43
|
+
lifetime_in_seconds: int = LIFETIME,
|
|
44
|
+
**kwargs,
|
|
45
|
+
) -> None:
|
|
46
|
+
"""Inits AuthByKeyPair class with private key.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
private_key: a byte array of der formats of private key, or an
|
|
50
|
+
object that implements the `RSAPrivateKey` interface.
|
|
51
|
+
lifetime_in_seconds: number of seconds the JWT token will be valid
|
|
52
|
+
"""
|
|
53
|
+
super().__init__(
|
|
54
|
+
max_retry_attempts=int(
|
|
55
|
+
os.getenv(
|
|
56
|
+
"JWT_CNXN_RETRY_ATTEMPTS", AuthByKeyPair.DEFAULT_JWT_RETRY_ATTEMPTS
|
|
57
|
+
)
|
|
58
|
+
),
|
|
59
|
+
**kwargs,
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
# set internal socket timeout override
|
|
63
|
+
self._socket_timeout = int(
|
|
64
|
+
timedelta(
|
|
65
|
+
seconds=int(
|
|
66
|
+
os.getenv(
|
|
67
|
+
"JWT_CNXN_WAIT_TIME",
|
|
68
|
+
AuthByKeyPair.DEFAULT_JWT_CNXN_WAIT_TIME,
|
|
69
|
+
)
|
|
70
|
+
)
|
|
71
|
+
).total_seconds()
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
self._private_key: bytes | str | RSAPrivateKey | None = private_key
|
|
75
|
+
self._jwt_token = ""
|
|
76
|
+
self._jwt_token_exp = 0
|
|
77
|
+
self._lifetime = timedelta(
|
|
78
|
+
seconds=int(os.getenv("JWT_LIFETIME_IN_SECONDS", lifetime_in_seconds))
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
def reset_secrets(self) -> None:
|
|
82
|
+
self._private_key = None
|
|
83
|
+
|
|
84
|
+
@property
|
|
85
|
+
def type_(self) -> AuthType:
|
|
86
|
+
return AuthType.KEY_PAIR
|
|
87
|
+
|
|
88
|
+
def prepare(
|
|
89
|
+
self,
|
|
90
|
+
*,
|
|
91
|
+
account: str,
|
|
92
|
+
user: str,
|
|
93
|
+
**kwargs: Any,
|
|
94
|
+
) -> str:
|
|
95
|
+
if ".global" in account:
|
|
96
|
+
account = account.partition("-")[0]
|
|
97
|
+
else:
|
|
98
|
+
account = account.partition(".")[0]
|
|
99
|
+
account = account.upper()
|
|
100
|
+
user = user.upper()
|
|
101
|
+
|
|
102
|
+
now = datetime.now(timezone.utc).replace(tzinfo=None)
|
|
103
|
+
|
|
104
|
+
if isinstance(self._private_key, str):
|
|
105
|
+
try:
|
|
106
|
+
self._private_key = base64.b64decode(self._private_key)
|
|
107
|
+
except Exception as e:
|
|
108
|
+
raise ProgrammingError(
|
|
109
|
+
msg=f"Failed to decode private key: {e}\nPlease provide a valid "
|
|
110
|
+
"unencrypted rsa private key in base64-encoded DER format as a "
|
|
111
|
+
"str object",
|
|
112
|
+
errno=ER_INVALID_PRIVATE_KEY,
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
if isinstance(self._private_key, bytes):
|
|
116
|
+
try:
|
|
117
|
+
private_key = load_der_private_key(
|
|
118
|
+
data=self._private_key,
|
|
119
|
+
password=None,
|
|
120
|
+
backend=default_backend(),
|
|
121
|
+
)
|
|
122
|
+
except Exception as e:
|
|
123
|
+
raise ProgrammingError(
|
|
124
|
+
msg=f"Failed to load private key: {e}\nPlease provide a valid "
|
|
125
|
+
"unencrypted rsa private key in DER format as bytes object",
|
|
126
|
+
errno=ER_INVALID_PRIVATE_KEY,
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
if not isinstance(private_key, RSAPrivateKey):
|
|
130
|
+
raise ProgrammingError(
|
|
131
|
+
msg=f"Private key type ({private_key.__class__.__name__}) not supported."
|
|
132
|
+
"\nPlease provide a valid rsa private key in DER format as bytes "
|
|
133
|
+
"object",
|
|
134
|
+
errno=ER_INVALID_PRIVATE_KEY,
|
|
135
|
+
)
|
|
136
|
+
elif isinstance(self._private_key, RSAPrivateKey):
|
|
137
|
+
private_key = self._private_key
|
|
138
|
+
else:
|
|
139
|
+
raise TypeError(
|
|
140
|
+
f"Expected bytes or RSAPrivateKey, got {type(self._private_key)}"
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
public_key_fp = self.calculate_public_key_fingerprint(private_key)
|
|
144
|
+
|
|
145
|
+
self._jwt_token_exp = now + self._lifetime
|
|
146
|
+
payload = {
|
|
147
|
+
self.ISSUER: f"{account}.{user}.{public_key_fp}",
|
|
148
|
+
self.SUBJECT: f"{account}.{user}",
|
|
149
|
+
self.ISSUE_TIME: now,
|
|
150
|
+
self.EXPIRE_TIME: self._jwt_token_exp,
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
_jwt_token = jwt.encode(payload, private_key, algorithm=self.ALGORITHM)
|
|
154
|
+
|
|
155
|
+
# jwt.encode() returns bytes in pyjwt 1.x and a string
|
|
156
|
+
# in pyjwt 2.x
|
|
157
|
+
if isinstance(_jwt_token, bytes):
|
|
158
|
+
self._jwt_token = _jwt_token.decode("utf-8")
|
|
159
|
+
else:
|
|
160
|
+
self._jwt_token = _jwt_token
|
|
161
|
+
|
|
162
|
+
return self._jwt_token
|
|
163
|
+
|
|
164
|
+
def reauthenticate(self, **kwargs: Any) -> dict[str, bool]:
|
|
165
|
+
return {"success": False}
|
|
166
|
+
|
|
167
|
+
@staticmethod
|
|
168
|
+
def calculate_public_key_fingerprint(private_key):
|
|
169
|
+
# get public key bytes
|
|
170
|
+
public_key_der = private_key.public_key().public_bytes(
|
|
171
|
+
Encoding.DER, PublicFormat.SubjectPublicKeyInfo
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
# take sha256 on raw bytes and then do base64 encode
|
|
175
|
+
sha256hash = hashlib.sha256()
|
|
176
|
+
sha256hash.update(public_key_der)
|
|
177
|
+
|
|
178
|
+
public_key_fp = "SHA256:" + base64.b64encode(sha256hash.digest()).decode(
|
|
179
|
+
"utf-8"
|
|
180
|
+
)
|
|
181
|
+
logger.debug("Public key fingerprint is %s", public_key_fp)
|
|
182
|
+
|
|
183
|
+
return public_key_fp
|
|
184
|
+
|
|
185
|
+
def update_body(self, body: dict[Any, Any]) -> None:
|
|
186
|
+
body["data"]["AUTHENTICATOR"] = KEY_PAIR_AUTHENTICATOR
|
|
187
|
+
body["data"]["TOKEN"] = self._jwt_token
|
|
188
|
+
|
|
189
|
+
def assertion_content(self) -> str:
|
|
190
|
+
return self._jwt_token
|
|
191
|
+
|
|
192
|
+
def should_retry(self, count: int) -> bool:
|
|
193
|
+
return count < self._jwt_retry_attempts
|
|
194
|
+
|
|
195
|
+
def handle_timeout(
|
|
196
|
+
self,
|
|
197
|
+
*,
|
|
198
|
+
authenticator: str,
|
|
199
|
+
service_name: str | None,
|
|
200
|
+
account: str,
|
|
201
|
+
user: str,
|
|
202
|
+
password: str | None,
|
|
203
|
+
**kwargs: Any,
|
|
204
|
+
) -> None:
|
|
205
|
+
logger.debug("Invoking base timeout handler")
|
|
206
|
+
super().handle_timeout(
|
|
207
|
+
authenticator=authenticator,
|
|
208
|
+
service_name=service_name,
|
|
209
|
+
account=account,
|
|
210
|
+
user=user,
|
|
211
|
+
password=password,
|
|
212
|
+
delete_params=False,
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
logger.debug("Base timeout handler passed, preparing new token before retrying")
|
|
216
|
+
self.prepare(account=account, user=user)
|
|
217
|
+
|
|
218
|
+
@staticmethod
|
|
219
|
+
def can_handle_exception(op: OperationalError) -> bool:
|
|
220
|
+
if op.errno is ER_CONNECTION_TIMEOUT:
|
|
221
|
+
return True
|
|
222
|
+
return False
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from .by_plugin import AuthByPlugin, AuthType
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class AuthNoAuth(AuthByPlugin):
|
|
10
|
+
"""No-auth Authentication.
|
|
11
|
+
|
|
12
|
+
It is a dummy auth that requires no extra connection establishment.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
@property
|
|
16
|
+
def type_(self) -> AuthType:
|
|
17
|
+
return AuthType.NO_AUTH
|
|
18
|
+
|
|
19
|
+
@property
|
|
20
|
+
def assertion_content(self) -> str | None:
|
|
21
|
+
return None
|
|
22
|
+
|
|
23
|
+
def __init__(self) -> None:
|
|
24
|
+
super().__init__()
|
|
25
|
+
|
|
26
|
+
def reset_secrets(self) -> None:
|
|
27
|
+
pass
|
|
28
|
+
|
|
29
|
+
def prepare(
|
|
30
|
+
self,
|
|
31
|
+
**kwargs: Any,
|
|
32
|
+
) -> None:
|
|
33
|
+
pass
|
|
34
|
+
|
|
35
|
+
def reauthenticate(self, **kwargs: Any) -> dict[str, bool]:
|
|
36
|
+
return {"success": True}
|
|
37
|
+
|
|
38
|
+
def update_body(self, body: dict[Any, Any]) -> None:
|
|
39
|
+
pass
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from ..network import OAUTH_AUTHENTICATOR
|
|
7
|
+
from .by_plugin import AuthByPlugin, AuthType
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class AuthByOAuth(AuthByPlugin):
|
|
11
|
+
"""OAuth Based Authentication.
|
|
12
|
+
|
|
13
|
+
Works by accepting an OAuth token and using that to authenticate.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
@property
|
|
17
|
+
def type_(self) -> AuthType:
|
|
18
|
+
return AuthType.OAUTH
|
|
19
|
+
|
|
20
|
+
@property
|
|
21
|
+
def assertion_content(self) -> str | None:
|
|
22
|
+
"""Returns the token."""
|
|
23
|
+
return self._oauth_token
|
|
24
|
+
|
|
25
|
+
def __init__(self, oauth_token: str, **kwargs) -> None:
|
|
26
|
+
"""Initializes an instance with an OAuth Token."""
|
|
27
|
+
super().__init__(**kwargs)
|
|
28
|
+
self._oauth_token: str | None = oauth_token
|
|
29
|
+
|
|
30
|
+
def reset_secrets(self) -> None:
|
|
31
|
+
self._oauth_token = None
|
|
32
|
+
|
|
33
|
+
def prepare(
|
|
34
|
+
self,
|
|
35
|
+
**kwargs: Any,
|
|
36
|
+
) -> None:
|
|
37
|
+
"""Nothing to do here, token should be obtained outside the driver."""
|
|
38
|
+
pass
|
|
39
|
+
|
|
40
|
+
def reauthenticate(self, **kwargs: Any) -> dict[str, bool]:
|
|
41
|
+
return {"success": False}
|
|
42
|
+
|
|
43
|
+
def update_body(self, body: dict[Any, Any]) -> None:
|
|
44
|
+
"""Update some information required by OAuth.
|
|
45
|
+
|
|
46
|
+
OAuth needs the authenticator and token attributes set, as well as loginname, which is set already in auth.py.
|
|
47
|
+
"""
|
|
48
|
+
body["data"]["AUTHENTICATOR"] = OAUTH_AUTHENTICATOR
|
|
49
|
+
body["data"]["TOKEN"] = self._oauth_token
|