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,335 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import json
|
|
5
|
+
import logging
|
|
6
|
+
import time
|
|
7
|
+
from functools import partial
|
|
8
|
+
from typing import TYPE_CHECKING, Any, Callable
|
|
9
|
+
|
|
10
|
+
from ..compat import unescape, urlencode, urlsplit
|
|
11
|
+
from ..constants import (
|
|
12
|
+
HTTP_HEADER_ACCEPT,
|
|
13
|
+
HTTP_HEADER_CONTENT_TYPE,
|
|
14
|
+
HTTP_HEADER_SERVICE_NAME,
|
|
15
|
+
HTTP_HEADER_USER_AGENT,
|
|
16
|
+
)
|
|
17
|
+
from ..errorcode import ER_IDP_CONNECTION_ERROR, ER_INCORRECT_DESTINATION
|
|
18
|
+
from ..errors import DatabaseError, Error, RefreshTokenError
|
|
19
|
+
from ..network import CONTENT_TYPE_APPLICATION_JSON, PYTHON_CONNECTOR_USER_AGENT
|
|
20
|
+
from ..sqlstate import SQLSTATE_CONNECTION_WAS_NOT_ESTABLISHED
|
|
21
|
+
from . import Auth
|
|
22
|
+
from .by_plugin import AuthByPlugin, AuthType
|
|
23
|
+
|
|
24
|
+
if TYPE_CHECKING:
|
|
25
|
+
from .. import SnowflakeConnection
|
|
26
|
+
|
|
27
|
+
logger = logging.getLogger(__name__)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _is_prefix_equal(url1, url2):
|
|
31
|
+
"""Checks if URL prefixes are identical.
|
|
32
|
+
|
|
33
|
+
The scheme, hostname and port number are compared. If the port number is not specified and the scheme is https,
|
|
34
|
+
the port number is assumed to be 443.
|
|
35
|
+
"""
|
|
36
|
+
parsed_url1 = urlsplit(url1)
|
|
37
|
+
parsed_url2 = urlsplit(url2)
|
|
38
|
+
|
|
39
|
+
port1 = parsed_url1.port
|
|
40
|
+
if not port1 and parsed_url1.scheme == "https":
|
|
41
|
+
port1 = "443"
|
|
42
|
+
port2 = parsed_url1.port
|
|
43
|
+
if not port2 and parsed_url2.scheme == "https":
|
|
44
|
+
port2 = "443"
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
parsed_url1.hostname == parsed_url2.hostname
|
|
48
|
+
and port1 == port2
|
|
49
|
+
and parsed_url1.scheme == parsed_url2.scheme
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _get_post_back_url_from_html(html):
|
|
54
|
+
"""Gets the post back URL.
|
|
55
|
+
|
|
56
|
+
Since the HTML is not well-formed, minidom cannot be used to convert to
|
|
57
|
+
DOM. The first discovered form is assumed to be the form to post back
|
|
58
|
+
and the URL is taken from action attributes.
|
|
59
|
+
"""
|
|
60
|
+
logger.debug(html)
|
|
61
|
+
|
|
62
|
+
idx = html.find("<form")
|
|
63
|
+
start_idx = html.find('action="', idx)
|
|
64
|
+
end_idx = html.find('"', start_idx + 8)
|
|
65
|
+
return unescape(html[start_idx + 8 : end_idx])
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class AuthByOkta(AuthByPlugin):
|
|
69
|
+
"""Authenticate user by OKTA."""
|
|
70
|
+
|
|
71
|
+
def __init__(self, application: str, **kwargs) -> None:
|
|
72
|
+
super().__init__(**kwargs)
|
|
73
|
+
self._saml_response = None
|
|
74
|
+
self._application = application
|
|
75
|
+
|
|
76
|
+
def reset_secrets(self) -> None:
|
|
77
|
+
pass
|
|
78
|
+
|
|
79
|
+
@property
|
|
80
|
+
def type_(self) -> AuthType:
|
|
81
|
+
return AuthType.OKTA
|
|
82
|
+
|
|
83
|
+
@property
|
|
84
|
+
def assertion_content(self) -> str:
|
|
85
|
+
return self._saml_response
|
|
86
|
+
|
|
87
|
+
def update_body(self, body: dict[Any, Any]) -> None:
|
|
88
|
+
body["data"]["RAW_SAML_RESPONSE"] = self._saml_response
|
|
89
|
+
|
|
90
|
+
def prepare(
|
|
91
|
+
self,
|
|
92
|
+
*,
|
|
93
|
+
conn: SnowflakeConnection,
|
|
94
|
+
authenticator: str,
|
|
95
|
+
service_name: str | None,
|
|
96
|
+
account: str,
|
|
97
|
+
user: str,
|
|
98
|
+
password: str,
|
|
99
|
+
**kwargs: Any,
|
|
100
|
+
) -> None:
|
|
101
|
+
"""SAML Authentication.
|
|
102
|
+
|
|
103
|
+
Steps are:
|
|
104
|
+
1. query GS to obtain IDP token and SSO url
|
|
105
|
+
2. IMPORTANT Client side validation:
|
|
106
|
+
validate both token url and sso url contains same prefix
|
|
107
|
+
(protocol + host + port) as the given authenticator url.
|
|
108
|
+
Explanation:
|
|
109
|
+
This provides a way for the user to 'authenticate' the IDP it is
|
|
110
|
+
sending his/her credentials to. Without such a check, the user could
|
|
111
|
+
be coerced to provide credentials to an IDP impersonator.
|
|
112
|
+
3. query IDP token url to authenticate and retrieve access token
|
|
113
|
+
4. given access token, query IDP URL snowflake app to get SAML response
|
|
114
|
+
5. IMPORTANT Client side validation:
|
|
115
|
+
validate the post back url come back with the SAML response
|
|
116
|
+
contains the same prefix as the Snowflake's server url, which is the
|
|
117
|
+
intended destination url to Snowflake.
|
|
118
|
+
Explanation:
|
|
119
|
+
This emulates the behavior of IDP initiated login flow in the user
|
|
120
|
+
browser where the IDP instructs the browser to POST the SAML
|
|
121
|
+
assertion to the specific SP endpoint. This is critical in
|
|
122
|
+
preventing a SAML assertion issued to one SP from being sent to
|
|
123
|
+
another SP.
|
|
124
|
+
"""
|
|
125
|
+
logger.debug("authenticating by SAML")
|
|
126
|
+
headers, sso_url, token_url = self._step1(
|
|
127
|
+
conn,
|
|
128
|
+
authenticator,
|
|
129
|
+
service_name,
|
|
130
|
+
account,
|
|
131
|
+
user,
|
|
132
|
+
)
|
|
133
|
+
self._step2(conn, authenticator, sso_url, token_url)
|
|
134
|
+
response_html = self._step4(
|
|
135
|
+
conn,
|
|
136
|
+
partial(self._step3, conn, headers, token_url, user, password),
|
|
137
|
+
sso_url,
|
|
138
|
+
)
|
|
139
|
+
self._step5(conn, response_html)
|
|
140
|
+
|
|
141
|
+
def reauthenticate(self, **kwargs: Any) -> dict[str, bool]:
|
|
142
|
+
return {"success": False}
|
|
143
|
+
|
|
144
|
+
def _step1(
|
|
145
|
+
self,
|
|
146
|
+
conn: SnowflakeConnection,
|
|
147
|
+
authenticator: str,
|
|
148
|
+
service_name: str | None,
|
|
149
|
+
account: str,
|
|
150
|
+
user: str,
|
|
151
|
+
) -> tuple[dict[str, str], str, str]:
|
|
152
|
+
logger.debug("step 1: query GS to obtain IDP token and SSO url")
|
|
153
|
+
|
|
154
|
+
headers = {
|
|
155
|
+
HTTP_HEADER_CONTENT_TYPE: CONTENT_TYPE_APPLICATION_JSON,
|
|
156
|
+
HTTP_HEADER_ACCEPT: CONTENT_TYPE_APPLICATION_JSON,
|
|
157
|
+
HTTP_HEADER_USER_AGENT: PYTHON_CONNECTOR_USER_AGENT,
|
|
158
|
+
}
|
|
159
|
+
if service_name:
|
|
160
|
+
headers[HTTP_HEADER_SERVICE_NAME] = service_name
|
|
161
|
+
url = "/session/authenticator-request"
|
|
162
|
+
body = Auth.base_auth_data(
|
|
163
|
+
user,
|
|
164
|
+
account,
|
|
165
|
+
conn.application,
|
|
166
|
+
conn._internal_application_name,
|
|
167
|
+
conn._internal_application_version,
|
|
168
|
+
conn._ocsp_mode(),
|
|
169
|
+
conn.login_timeout,
|
|
170
|
+
conn._network_timeout,
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
body["data"]["AUTHENTICATOR"] = authenticator
|
|
174
|
+
logger.debug(
|
|
175
|
+
"account=%s, authenticator=%s",
|
|
176
|
+
account,
|
|
177
|
+
authenticator,
|
|
178
|
+
)
|
|
179
|
+
ret = conn._rest._post_request(
|
|
180
|
+
url,
|
|
181
|
+
headers,
|
|
182
|
+
json.dumps(body),
|
|
183
|
+
timeout=conn._rest._connection.login_timeout,
|
|
184
|
+
socket_timeout=conn._rest._connection.login_timeout,
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
if not ret["success"]:
|
|
188
|
+
self._handle_failure(conn=conn, ret=ret)
|
|
189
|
+
|
|
190
|
+
data = ret["data"]
|
|
191
|
+
token_url = data["tokenUrl"]
|
|
192
|
+
sso_url = data["ssoUrl"]
|
|
193
|
+
return headers, sso_url, token_url
|
|
194
|
+
|
|
195
|
+
def _step2(
|
|
196
|
+
self,
|
|
197
|
+
conn: SnowflakeConnection,
|
|
198
|
+
authenticator: str,
|
|
199
|
+
sso_url: str,
|
|
200
|
+
token_url: str,
|
|
201
|
+
) -> None:
|
|
202
|
+
logger.debug(
|
|
203
|
+
"step 2: validate Token and SSO URL has the same prefix as authenticator"
|
|
204
|
+
)
|
|
205
|
+
if not _is_prefix_equal(authenticator, token_url) or not _is_prefix_equal(
|
|
206
|
+
authenticator, sso_url
|
|
207
|
+
):
|
|
208
|
+
Error.errorhandler_wrapper(
|
|
209
|
+
conn._rest._connection,
|
|
210
|
+
None,
|
|
211
|
+
DatabaseError,
|
|
212
|
+
{
|
|
213
|
+
"msg": (
|
|
214
|
+
"The specified authenticator is not supported: "
|
|
215
|
+
f"{authenticator}, token_url: {token_url}, "
|
|
216
|
+
f"sso_url: {sso_url}"
|
|
217
|
+
),
|
|
218
|
+
"errno": ER_IDP_CONNECTION_ERROR,
|
|
219
|
+
"sqlstate": SQLSTATE_CONNECTION_WAS_NOT_ESTABLISHED,
|
|
220
|
+
},
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
@staticmethod
|
|
224
|
+
def _step3(
|
|
225
|
+
conn: SnowflakeConnection,
|
|
226
|
+
headers: dict[str, str],
|
|
227
|
+
token_url: str,
|
|
228
|
+
user: str,
|
|
229
|
+
password: str,
|
|
230
|
+
) -> str:
|
|
231
|
+
logger.debug(
|
|
232
|
+
"step 3: query IDP token url to authenticate and " "retrieve access token"
|
|
233
|
+
)
|
|
234
|
+
data = {
|
|
235
|
+
"username": user,
|
|
236
|
+
"password": password,
|
|
237
|
+
}
|
|
238
|
+
ret = conn._rest.fetch(
|
|
239
|
+
"post",
|
|
240
|
+
token_url,
|
|
241
|
+
headers,
|
|
242
|
+
data=json.dumps(data),
|
|
243
|
+
timeout=conn._rest._connection.login_timeout,
|
|
244
|
+
socket_timeout=conn._rest._connection.login_timeout,
|
|
245
|
+
catch_okta_unauthorized_error=True,
|
|
246
|
+
)
|
|
247
|
+
one_time_token = ret.get("sessionToken", ret.get("cookieToken"))
|
|
248
|
+
if not one_time_token:
|
|
249
|
+
Error.errorhandler_wrapper(
|
|
250
|
+
conn._rest._connection,
|
|
251
|
+
None,
|
|
252
|
+
DatabaseError,
|
|
253
|
+
{
|
|
254
|
+
"msg": (
|
|
255
|
+
"The authentication failed for {user} "
|
|
256
|
+
"by {token_url}.".format(
|
|
257
|
+
token_url=token_url,
|
|
258
|
+
user=user,
|
|
259
|
+
)
|
|
260
|
+
),
|
|
261
|
+
"errno": ER_IDP_CONNECTION_ERROR,
|
|
262
|
+
"sqlstate": SQLSTATE_CONNECTION_WAS_NOT_ESTABLISHED,
|
|
263
|
+
},
|
|
264
|
+
)
|
|
265
|
+
return one_time_token
|
|
266
|
+
|
|
267
|
+
@staticmethod
|
|
268
|
+
def _step4(
|
|
269
|
+
conn: SnowflakeConnection,
|
|
270
|
+
generate_one_time_token: Callable,
|
|
271
|
+
sso_url: str,
|
|
272
|
+
) -> dict[Any, Any]:
|
|
273
|
+
logger.debug("step 4: query IDP URL snowflake app to get SAML " "response")
|
|
274
|
+
timeout_time = time.time() + conn.login_timeout if conn.login_timeout else None
|
|
275
|
+
response_html = {}
|
|
276
|
+
origin_sso_url = sso_url
|
|
277
|
+
while timeout_time is None or time.time() < timeout_time:
|
|
278
|
+
try:
|
|
279
|
+
url_parameters = {
|
|
280
|
+
"RelayState": "/some/deep/link",
|
|
281
|
+
"onetimetoken": generate_one_time_token(),
|
|
282
|
+
}
|
|
283
|
+
sso_url = origin_sso_url + "?" + urlencode(url_parameters)
|
|
284
|
+
headers = {
|
|
285
|
+
HTTP_HEADER_ACCEPT: "*/*",
|
|
286
|
+
}
|
|
287
|
+
remaining_timeout = timeout_time - time.time() if timeout_time else None
|
|
288
|
+
response_html = conn._rest.fetch(
|
|
289
|
+
"get",
|
|
290
|
+
sso_url,
|
|
291
|
+
headers,
|
|
292
|
+
timeout=remaining_timeout,
|
|
293
|
+
socket_timeout=remaining_timeout,
|
|
294
|
+
is_raw_text=True,
|
|
295
|
+
is_okta_authentication=True,
|
|
296
|
+
)
|
|
297
|
+
break
|
|
298
|
+
except RefreshTokenError:
|
|
299
|
+
logger.debug("step4: refresh token for re-authentication")
|
|
300
|
+
return response_html
|
|
301
|
+
|
|
302
|
+
def _step5(
|
|
303
|
+
self,
|
|
304
|
+
conn: SnowflakeConnection,
|
|
305
|
+
response_html: str,
|
|
306
|
+
) -> None:
|
|
307
|
+
logger.debug("step 5: validate post_back_url matches Snowflake URL")
|
|
308
|
+
post_back_url = _get_post_back_url_from_html(response_html)
|
|
309
|
+
full_url = "{protocol}://{host}:{port}".format(
|
|
310
|
+
protocol=conn._rest._protocol,
|
|
311
|
+
host=conn._rest._host,
|
|
312
|
+
port=conn._rest._port,
|
|
313
|
+
)
|
|
314
|
+
if not getattr(conn, "_disable_saml_url_check", False) and not _is_prefix_equal(
|
|
315
|
+
post_back_url, full_url
|
|
316
|
+
):
|
|
317
|
+
Error.errorhandler_wrapper(
|
|
318
|
+
conn._rest._connection,
|
|
319
|
+
None,
|
|
320
|
+
DatabaseError,
|
|
321
|
+
{
|
|
322
|
+
"msg": (
|
|
323
|
+
"The specified authenticator and destination "
|
|
324
|
+
"URL in the SAML assertion do not match: "
|
|
325
|
+
"expected: {url}, "
|
|
326
|
+
"post back: {post_back_url}".format(
|
|
327
|
+
url=full_url,
|
|
328
|
+
post_back_url=post_back_url,
|
|
329
|
+
)
|
|
330
|
+
),
|
|
331
|
+
"errno": ER_INCORRECT_DESTINATION,
|
|
332
|
+
"sqlstate": SQLSTATE_CONNECTION_WAS_NOT_ESTABLISHED,
|
|
333
|
+
},
|
|
334
|
+
)
|
|
335
|
+
self._saml_response = response_html
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import typing
|
|
4
|
+
|
|
5
|
+
from snowflake.connector.network import PROGRAMMATIC_ACCESS_TOKEN
|
|
6
|
+
|
|
7
|
+
from .by_plugin import AuthByPlugin, AuthType
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class AuthByPAT(AuthByPlugin):
|
|
11
|
+
|
|
12
|
+
def __init__(self, pat_token: str, **kwargs) -> None:
|
|
13
|
+
super().__init__(**kwargs)
|
|
14
|
+
self._pat_token: str | None = pat_token
|
|
15
|
+
|
|
16
|
+
def type_(self) -> AuthType:
|
|
17
|
+
return AuthType.PAT
|
|
18
|
+
|
|
19
|
+
def reset_secrets(self) -> None:
|
|
20
|
+
self._pat_token = None
|
|
21
|
+
|
|
22
|
+
def update_body(self, body: dict[typing.Any, typing.Any]) -> None:
|
|
23
|
+
body["data"]["AUTHENTICATOR"] = PROGRAMMATIC_ACCESS_TOKEN
|
|
24
|
+
body["data"]["TOKEN"] = self._pat_token
|
|
25
|
+
|
|
26
|
+
def prepare(
|
|
27
|
+
self,
|
|
28
|
+
**kwargs: typing.Any,
|
|
29
|
+
) -> None:
|
|
30
|
+
"""Nothing to do here, token should be obtained outside the driver."""
|
|
31
|
+
pass
|
|
32
|
+
|
|
33
|
+
def reauthenticate(self, **kwargs: typing.Any) -> dict[str, bool]:
|
|
34
|
+
return {"success": False}
|
|
35
|
+
|
|
36
|
+
@property
|
|
37
|
+
def assertion_content(self) -> str | None:
|
|
38
|
+
"""Returns the token."""
|
|
39
|
+
return self._pat_token
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import logging
|
|
5
|
+
from typing import TYPE_CHECKING, Any
|
|
6
|
+
|
|
7
|
+
from ..errorcode import ER_NO_PASSWORD
|
|
8
|
+
from ..errors import ProgrammingError
|
|
9
|
+
from .by_plugin import AuthByPlugin, AuthType
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from .. import SnowflakeConnection
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
MFA_TOKEN = "MFATOKEN"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class AuthByUsrPwdMfa(AuthByPlugin):
|
|
20
|
+
"""Username & password & mfa authenticator."""
|
|
21
|
+
|
|
22
|
+
@property
|
|
23
|
+
def assertion_content(self) -> str:
|
|
24
|
+
return "*********"
|
|
25
|
+
|
|
26
|
+
def __init__(
|
|
27
|
+
self,
|
|
28
|
+
password: str,
|
|
29
|
+
mfa_token: str | None = None,
|
|
30
|
+
**kwargs,
|
|
31
|
+
) -> None:
|
|
32
|
+
"""Initializes and instance with a password and a mfa token."""
|
|
33
|
+
super().__init__(**kwargs)
|
|
34
|
+
self._password: str | None = password
|
|
35
|
+
self._mfa_token: str | None = mfa_token
|
|
36
|
+
|
|
37
|
+
def reset_secrets(self) -> None:
|
|
38
|
+
self._password = None
|
|
39
|
+
self._mfa_token = None
|
|
40
|
+
|
|
41
|
+
@property
|
|
42
|
+
def type_(self) -> AuthType:
|
|
43
|
+
return AuthType.USR_PWD_MFA
|
|
44
|
+
|
|
45
|
+
def prepare(
|
|
46
|
+
self,
|
|
47
|
+
*,
|
|
48
|
+
conn: SnowflakeConnection,
|
|
49
|
+
**kwargs: Any,
|
|
50
|
+
) -> None:
|
|
51
|
+
if conn._rest and conn._rest.mfa_token:
|
|
52
|
+
self._mfa_token = conn._rest.mfa_token
|
|
53
|
+
|
|
54
|
+
def reauthenticate(self, **kwargs) -> dict[str, bool]:
|
|
55
|
+
return {"success": False}
|
|
56
|
+
|
|
57
|
+
def update_body(self, body: dict[Any, Any]) -> None:
|
|
58
|
+
"""Sets the password and mfa_token if available.
|
|
59
|
+
|
|
60
|
+
Don't set body['data']['AUTHENTICATOR'], since this is still snowflake default authenticator.
|
|
61
|
+
"""
|
|
62
|
+
if not self._password:
|
|
63
|
+
raise ProgrammingError(
|
|
64
|
+
msg="Password for username password authenticator is empty.",
|
|
65
|
+
errno=ER_NO_PASSWORD,
|
|
66
|
+
)
|
|
67
|
+
body["data"]["PASSWORD"] = self._password
|
|
68
|
+
if self._mfa_token:
|
|
69
|
+
body["data"]["TOKEN"] = self._mfa_token
|