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.
Files changed (246) hide show
  1. snowflake/connector/__init__.py +96 -0
  2. snowflake/connector/_query_context_cache.py +276 -0
  3. snowflake/connector/_sql_util.py +48 -0
  4. snowflake/connector/_utils.py +70 -0
  5. snowflake/connector/arrow_context.py +203 -0
  6. snowflake/connector/auth/__init__.py +53 -0
  7. snowflake/connector/auth/_auth.py +586 -0
  8. snowflake/connector/auth/_http_server.py +220 -0
  9. snowflake/connector/auth/_oauth_base.py +400 -0
  10. snowflake/connector/auth/by_plugin.py +219 -0
  11. snowflake/connector/auth/default.py +36 -0
  12. snowflake/connector/auth/idtoken.py +72 -0
  13. snowflake/connector/auth/keypair.py +222 -0
  14. snowflake/connector/auth/no_auth.py +39 -0
  15. snowflake/connector/auth/oauth.py +49 -0
  16. snowflake/connector/auth/oauth_code.py +479 -0
  17. snowflake/connector/auth/oauth_credentials.py +66 -0
  18. snowflake/connector/auth/okta.py +335 -0
  19. snowflake/connector/auth/pat.py +39 -0
  20. snowflake/connector/auth/usrpwdmfa.py +69 -0
  21. snowflake/connector/auth/webbrowser.py +502 -0
  22. snowflake/connector/auth/workload_identity.py +94 -0
  23. snowflake/connector/azure_storage_client.py +276 -0
  24. snowflake/connector/backoff_policies.py +141 -0
  25. snowflake/connector/bind_upload_agent.py +89 -0
  26. snowflake/connector/cache.py +696 -0
  27. snowflake/connector/compat.py +127 -0
  28. snowflake/connector/config_manager.py +496 -0
  29. snowflake/connector/connection.py +2273 -0
  30. snowflake/connector/connection_diagnostic.py +776 -0
  31. snowflake/connector/constants.py +442 -0
  32. snowflake/connector/converter.py +785 -0
  33. snowflake/connector/converter_issue23517.py +87 -0
  34. snowflake/connector/converter_null.py +14 -0
  35. snowflake/connector/converter_snowsql.py +205 -0
  36. snowflake/connector/cursor.py +1951 -0
  37. snowflake/connector/dbapi.py +53 -0
  38. snowflake/connector/description.py +19 -0
  39. snowflake/connector/direct_file_operation_utils.py +88 -0
  40. snowflake/connector/encryption_util.py +220 -0
  41. snowflake/connector/errorcode.py +91 -0
  42. snowflake/connector/errors.py +615 -0
  43. snowflake/connector/externals_utils/__init__.py +0 -0
  44. snowflake/connector/externals_utils/externals_setup.py +27 -0
  45. snowflake/connector/feature.py +4 -0
  46. snowflake/connector/file_compression_type.py +118 -0
  47. snowflake/connector/file_lock.py +72 -0
  48. snowflake/connector/file_transfer_agent.py +1215 -0
  49. snowflake/connector/file_util.py +153 -0
  50. snowflake/connector/gcs_storage_client.py +474 -0
  51. snowflake/connector/gzip_decoder.py +85 -0
  52. snowflake/connector/local_storage_client.py +90 -0
  53. snowflake/connector/log_configuration.py +60 -0
  54. snowflake/connector/logging_utils/__init__.py +0 -0
  55. snowflake/connector/logging_utils/filters.py +72 -0
  56. snowflake/connector/nanoarrow_arrow_iterator.cp313-win_amd64.pyd +0 -0
  57. snowflake/connector/nanoarrow_cpp/ArrowIterator/ArrayConverter.cpp +60 -0
  58. snowflake/connector/nanoarrow_cpp/ArrowIterator/ArrayConverter.hpp +29 -0
  59. snowflake/connector/nanoarrow_cpp/ArrowIterator/BinaryConverter.cpp +19 -0
  60. snowflake/connector/nanoarrow_cpp/ArrowIterator/BinaryConverter.hpp +26 -0
  61. snowflake/connector/nanoarrow_cpp/ArrowIterator/BooleanConverter.cpp +21 -0
  62. snowflake/connector/nanoarrow_cpp/ArrowIterator/BooleanConverter.hpp +23 -0
  63. snowflake/connector/nanoarrow_cpp/ArrowIterator/CArrowChunkIterator.cpp +557 -0
  64. snowflake/connector/nanoarrow_cpp/ArrowIterator/CArrowChunkIterator.hpp +98 -0
  65. snowflake/connector/nanoarrow_cpp/ArrowIterator/CArrowIterator.cpp +125 -0
  66. snowflake/connector/nanoarrow_cpp/ArrowIterator/CArrowIterator.hpp +115 -0
  67. snowflake/connector/nanoarrow_cpp/ArrowIterator/CArrowTableIterator.cpp +1005 -0
  68. snowflake/connector/nanoarrow_cpp/ArrowIterator/CArrowTableIterator.hpp +127 -0
  69. snowflake/connector/nanoarrow_cpp/ArrowIterator/DateConverter.cpp +47 -0
  70. snowflake/connector/nanoarrow_cpp/ArrowIterator/DateConverter.hpp +46 -0
  71. snowflake/connector/nanoarrow_cpp/ArrowIterator/DecFloatConverter.cpp +83 -0
  72. snowflake/connector/nanoarrow_cpp/ArrowIterator/DecFloatConverter.hpp +35 -0
  73. snowflake/connector/nanoarrow_cpp/ArrowIterator/DecimalConverter.cpp +97 -0
  74. snowflake/connector/nanoarrow_cpp/ArrowIterator/DecimalConverter.hpp +72 -0
  75. snowflake/connector/nanoarrow_cpp/ArrowIterator/FixedSizeListConverter.cpp +73 -0
  76. snowflake/connector/nanoarrow_cpp/ArrowIterator/FixedSizeListConverter.hpp +28 -0
  77. snowflake/connector/nanoarrow_cpp/ArrowIterator/FloatConverter.cpp +30 -0
  78. snowflake/connector/nanoarrow_cpp/ArrowIterator/FloatConverter.hpp +35 -0
  79. snowflake/connector/nanoarrow_cpp/ArrowIterator/IColumnConverter.hpp +17 -0
  80. snowflake/connector/nanoarrow_cpp/ArrowIterator/IntConverter.cpp +23 -0
  81. snowflake/connector/nanoarrow_cpp/ArrowIterator/IntConverter.hpp +45 -0
  82. snowflake/connector/nanoarrow_cpp/ArrowIterator/IntervalConverter.cpp +71 -0
  83. snowflake/connector/nanoarrow_cpp/ArrowIterator/IntervalConverter.hpp +56 -0
  84. snowflake/connector/nanoarrow_cpp/ArrowIterator/LICENSE.txt +209 -0
  85. snowflake/connector/nanoarrow_cpp/ArrowIterator/MapConverter.cpp +75 -0
  86. snowflake/connector/nanoarrow_cpp/ArrowIterator/MapConverter.hpp +30 -0
  87. snowflake/connector/nanoarrow_cpp/ArrowIterator/ObjectConverter.cpp +46 -0
  88. snowflake/connector/nanoarrow_cpp/ArrowIterator/ObjectConverter.hpp +29 -0
  89. snowflake/connector/nanoarrow_cpp/ArrowIterator/Python/Common.cpp +8 -0
  90. snowflake/connector/nanoarrow_cpp/ArrowIterator/Python/Common.hpp +95 -0
  91. snowflake/connector/nanoarrow_cpp/ArrowIterator/Python/Helpers.cpp +57 -0
  92. snowflake/connector/nanoarrow_cpp/ArrowIterator/Python/Helpers.hpp +36 -0
  93. snowflake/connector/nanoarrow_cpp/ArrowIterator/SnowflakeType.cpp +34 -0
  94. snowflake/connector/nanoarrow_cpp/ArrowIterator/SnowflakeType.hpp +48 -0
  95. snowflake/connector/nanoarrow_cpp/ArrowIterator/StringConverter.cpp +19 -0
  96. snowflake/connector/nanoarrow_cpp/ArrowIterator/StringConverter.hpp +26 -0
  97. snowflake/connector/nanoarrow_cpp/ArrowIterator/TimeConverter.cpp +36 -0
  98. snowflake/connector/nanoarrow_cpp/ArrowIterator/TimeConverter.hpp +31 -0
  99. snowflake/connector/nanoarrow_cpp/ArrowIterator/TimeStampConverter.cpp +346 -0
  100. snowflake/connector/nanoarrow_cpp/ArrowIterator/TimeStampConverter.hpp +145 -0
  101. snowflake/connector/nanoarrow_cpp/ArrowIterator/Util/macros.hpp +14 -0
  102. snowflake/connector/nanoarrow_cpp/ArrowIterator/Util/time.cpp +65 -0
  103. snowflake/connector/nanoarrow_cpp/ArrowIterator/Util/time.hpp +68 -0
  104. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_accessors.h +101 -0
  105. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_alloc.h +127 -0
  106. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_assert.h +45 -0
  107. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_builder.h +1908 -0
  108. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_emitter.h +215 -0
  109. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_endian.h +125 -0
  110. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_epilogue.h +7 -0
  111. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_flatbuffers.h +55 -0
  112. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_identifier.h +148 -0
  113. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_iov.h +31 -0
  114. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_prologue.h +8 -0
  115. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_refmap.h +144 -0
  116. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_rtconfig.h +162 -0
  117. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_types.h +97 -0
  118. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_verifier.h +239 -0
  119. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/flatcc_portable.h +14 -0
  120. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/paligned_alloc.h +210 -0
  121. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pattributes.h +84 -0
  122. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pdiagnostic.h +84 -0
  123. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pdiagnostic_pop.h +20 -0
  124. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pdiagnostic_push.h +51 -0
  125. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pendian.h +206 -0
  126. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pendian_detect.h +118 -0
  127. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pinline.h +19 -0
  128. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pinttypes.h +52 -0
  129. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/portable.h +2 -0
  130. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/portable_basic.h +25 -0
  131. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pstatic_assert.h +67 -0
  132. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pstdalign.h +162 -0
  133. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pstdint.h +898 -0
  134. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/punaligned.h +190 -0
  135. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pversion.h +6 -0
  136. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pwarnings.h +52 -0
  137. snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc.c +3204 -0
  138. snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow.c +3217 -0
  139. snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow.h +3618 -0
  140. snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow.hpp +379 -0
  141. snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow_arrow_iterator.pyx +256 -0
  142. snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow_device.c +512 -0
  143. snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow_device.h +350 -0
  144. snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow_ipc.c +33273 -0
  145. snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow_ipc.h +397 -0
  146. snowflake/connector/nanoarrow_cpp/Logging/logging.cpp +100 -0
  147. snowflake/connector/nanoarrow_cpp/Logging/logging.hpp +49 -0
  148. snowflake/connector/network.py +1297 -0
  149. snowflake/connector/ocsp_asn1crypto.py +447 -0
  150. snowflake/connector/ocsp_snowflake.py +1883 -0
  151. snowflake/connector/options.py +131 -0
  152. snowflake/connector/pandas_tools.py +732 -0
  153. snowflake/connector/proxy.py +43 -0
  154. snowflake/connector/py.typed +0 -0
  155. snowflake/connector/result_batch.py +786 -0
  156. snowflake/connector/result_set.py +319 -0
  157. snowflake/connector/s3_storage_client.py +605 -0
  158. snowflake/connector/secret_detector.py +181 -0
  159. snowflake/connector/sf_dirs.py +64 -0
  160. snowflake/connector/sfbinaryformat.py +35 -0
  161. snowflake/connector/sfdatetime.py +345 -0
  162. snowflake/connector/snow_logging.py +144 -0
  163. snowflake/connector/sqlstate.py +9 -0
  164. snowflake/connector/ssd_internal_keys.py +30 -0
  165. snowflake/connector/ssl_wrap_socket.py +134 -0
  166. snowflake/connector/storage_client.py +478 -0
  167. snowflake/connector/telemetry.py +249 -0
  168. snowflake/connector/telemetry_oob.py +543 -0
  169. snowflake/connector/test_util.py +30 -0
  170. snowflake/connector/time_util.py +159 -0
  171. snowflake/connector/token_cache.py +402 -0
  172. snowflake/connector/tool/__init__.py +0 -0
  173. snowflake/connector/tool/dump_certs.py +57 -0
  174. snowflake/connector/tool/dump_ocsp_response.py +139 -0
  175. snowflake/connector/tool/dump_ocsp_response_cache.py +194 -0
  176. snowflake/connector/tool/probe_connection.py +69 -0
  177. snowflake/connector/url_util.py +49 -0
  178. snowflake/connector/util_text.py +301 -0
  179. snowflake/connector/vendored/__init__.py +3 -0
  180. snowflake/connector/vendored/requests/LICENSE +175 -0
  181. snowflake/connector/vendored/requests/__init__.py +169 -0
  182. snowflake/connector/vendored/requests/__version__.py +14 -0
  183. snowflake/connector/vendored/requests/_internal_utils.py +50 -0
  184. snowflake/connector/vendored/requests/adapters.py +537 -0
  185. snowflake/connector/vendored/requests/api.py +157 -0
  186. snowflake/connector/vendored/requests/auth.py +315 -0
  187. snowflake/connector/vendored/requests/certs.py +17 -0
  188. snowflake/connector/vendored/requests/compat.py +79 -0
  189. snowflake/connector/vendored/requests/cookies.py +561 -0
  190. snowflake/connector/vendored/requests/exceptions.py +140 -0
  191. snowflake/connector/vendored/requests/help.py +134 -0
  192. snowflake/connector/vendored/requests/hooks.py +33 -0
  193. snowflake/connector/vendored/requests/models.py +1033 -0
  194. snowflake/connector/vendored/requests/sessions.py +833 -0
  195. snowflake/connector/vendored/requests/status_codes.py +128 -0
  196. snowflake/connector/vendored/requests/structures.py +99 -0
  197. snowflake/connector/vendored/requests/utils.py +1093 -0
  198. snowflake/connector/vendored/urllib3/LICENSE.txt +21 -0
  199. snowflake/connector/vendored/urllib3/__init__.py +85 -0
  200. snowflake/connector/vendored/urllib3/_collections.py +355 -0
  201. snowflake/connector/vendored/urllib3/_version.py +2 -0
  202. snowflake/connector/vendored/urllib3/connection.py +572 -0
  203. snowflake/connector/vendored/urllib3/connectionpool.py +1137 -0
  204. snowflake/connector/vendored/urllib3/contrib/__init__.py +0 -0
  205. snowflake/connector/vendored/urllib3/contrib/_appengine_environ.py +36 -0
  206. snowflake/connector/vendored/urllib3/contrib/_securetransport/__init__.py +0 -0
  207. snowflake/connector/vendored/urllib3/contrib/_securetransport/bindings.py +519 -0
  208. snowflake/connector/vendored/urllib3/contrib/_securetransport/low_level.py +397 -0
  209. snowflake/connector/vendored/urllib3/contrib/appengine.py +314 -0
  210. snowflake/connector/vendored/urllib3/contrib/ntlmpool.py +130 -0
  211. snowflake/connector/vendored/urllib3/contrib/pyopenssl.py +509 -0
  212. snowflake/connector/vendored/urllib3/contrib/securetransport.py +920 -0
  213. snowflake/connector/vendored/urllib3/contrib/socks.py +216 -0
  214. snowflake/connector/vendored/urllib3/exceptions.py +323 -0
  215. snowflake/connector/vendored/urllib3/fields.py +274 -0
  216. snowflake/connector/vendored/urllib3/filepost.py +98 -0
  217. snowflake/connector/vendored/urllib3/packages/__init__.py +0 -0
  218. snowflake/connector/vendored/urllib3/packages/backports/__init__.py +0 -0
  219. snowflake/connector/vendored/urllib3/packages/backports/makefile.py +51 -0
  220. snowflake/connector/vendored/urllib3/packages/backports/weakref_finalize.py +155 -0
  221. snowflake/connector/vendored/urllib3/packages/six.py +1076 -0
  222. snowflake/connector/vendored/urllib3/poolmanager.py +540 -0
  223. snowflake/connector/vendored/urllib3/request.py +191 -0
  224. snowflake/connector/vendored/urllib3/response.py +885 -0
  225. snowflake/connector/vendored/urllib3/util/__init__.py +49 -0
  226. snowflake/connector/vendored/urllib3/util/connection.py +156 -0
  227. snowflake/connector/vendored/urllib3/util/proxy.py +57 -0
  228. snowflake/connector/vendored/urllib3/util/queue.py +22 -0
  229. snowflake/connector/vendored/urllib3/util/request.py +146 -0
  230. snowflake/connector/vendored/urllib3/util/response.py +107 -0
  231. snowflake/connector/vendored/urllib3/util/retry.py +620 -0
  232. snowflake/connector/vendored/urllib3/util/ssl_.py +495 -0
  233. snowflake/connector/vendored/urllib3/util/ssl_match_hostname.py +159 -0
  234. snowflake/connector/vendored/urllib3/util/ssltransport.py +221 -0
  235. snowflake/connector/vendored/urllib3/util/timeout.py +271 -0
  236. snowflake/connector/vendored/urllib3/util/url.py +435 -0
  237. snowflake/connector/vendored/urllib3/util/wait.py +152 -0
  238. snowflake/connector/version.py +3 -0
  239. snowflake/connector/wif_util.py +407 -0
  240. snowflake_connector_python-3.16.0.dist-info/METADATA +1475 -0
  241. snowflake_connector_python-3.16.0.dist-info/RECORD +246 -0
  242. snowflake_connector_python-3.16.0.dist-info/WHEEL +5 -0
  243. snowflake_connector_python-3.16.0.dist-info/entry_points.txt +4 -0
  244. snowflake_connector_python-3.16.0.dist-info/licenses/LICENSE.txt +202 -0
  245. snowflake_connector_python-3.16.0.dist-info/licenses/NOTICE +8 -0
  246. snowflake_connector_python-3.16.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,502 @@
1
+ #!/usr/bin/env python
2
+ from __future__ import annotations
3
+
4
+ import base64
5
+ import json
6
+ import logging
7
+ import os
8
+ import secrets
9
+ import select
10
+ import socket
11
+ import time
12
+ import webbrowser
13
+ from types import ModuleType
14
+ from typing import TYPE_CHECKING, Any
15
+
16
+ from ..compat import IS_WINDOWS, parse_qs, urlencode, urlparse, urlsplit
17
+ from ..constants import (
18
+ HTTP_HEADER_ACCEPT,
19
+ HTTP_HEADER_CONTENT_TYPE,
20
+ HTTP_HEADER_SERVICE_NAME,
21
+ HTTP_HEADER_USER_AGENT,
22
+ )
23
+ from ..errorcode import (
24
+ ER_IDP_CONNECTION_ERROR,
25
+ ER_INVALID_VALUE,
26
+ ER_NO_HOSTNAME_FOUND,
27
+ ER_UNABLE_TO_OPEN_BROWSER,
28
+ )
29
+ from ..errors import OperationalError
30
+ from ..network import (
31
+ CONTENT_TYPE_APPLICATION_JSON,
32
+ EXTERNAL_BROWSER_AUTHENTICATOR,
33
+ PYTHON_CONNECTOR_USER_AGENT,
34
+ )
35
+ from ..url_util import is_valid_url
36
+ from . import Auth
37
+ from .by_plugin import AuthByPlugin, AuthType
38
+
39
+ if TYPE_CHECKING:
40
+ from .. import SnowflakeConnection
41
+
42
+ logger = logging.getLogger(__name__)
43
+
44
+ BUF_SIZE = 16384
45
+
46
+
47
+ # global state of web server that receives the SAML assertion from
48
+ # Snowflake server
49
+
50
+
51
+ class AuthByWebBrowser(AuthByPlugin):
52
+ """Authenticates user by web browser. Only used for SAML based authentication."""
53
+
54
+ def __init__(
55
+ self,
56
+ application: str,
57
+ webbrowser_pkg: ModuleType | None = None,
58
+ socket_pkg: type[socket.socket] | None = None,
59
+ protocol: str | None = None,
60
+ host: str | None = None,
61
+ port: str | None = None,
62
+ **kwargs,
63
+ ) -> None:
64
+ super().__init__(**kwargs)
65
+ self.consent_cache_id_token = True
66
+ self._token: str | None = None
67
+ self._application = application
68
+ self._proof_key = None
69
+ self._webbrowser: ModuleType = (
70
+ webbrowser if webbrowser_pkg is None else webbrowser_pkg
71
+ )
72
+ self._socket: type[socket.socket] = (
73
+ socket.socket if socket_pkg is None else socket_pkg
74
+ )
75
+ self._protocol = protocol
76
+ self._host = host
77
+ self._port = port
78
+ self._origin = None
79
+
80
+ def reset_secrets(self) -> None:
81
+ self._token = None
82
+
83
+ @property
84
+ def type_(self) -> AuthType:
85
+ return AuthType.EXTERNAL_BROWSER
86
+
87
+ @property
88
+ def assertion_content(self) -> str:
89
+ """Returns the token."""
90
+ return self._token
91
+
92
+ def update_body(self, body: dict[Any, Any]) -> None:
93
+ """Used by Auth to update the request that gets sent to /v1/login-request.
94
+
95
+ Args:
96
+ body: existing request dictionary
97
+ """
98
+ body["data"]["AUTHENTICATOR"] = EXTERNAL_BROWSER_AUTHENTICATOR
99
+ body["data"]["TOKEN"] = self._token
100
+ body["data"]["PROOF_KEY"] = self._proof_key
101
+
102
+ def prepare(
103
+ self,
104
+ *,
105
+ conn: SnowflakeConnection,
106
+ authenticator: str,
107
+ service_name: str | None,
108
+ account: str,
109
+ user: str,
110
+ **kwargs: Any,
111
+ ) -> None:
112
+ """Web Browser based Authentication."""
113
+ logger.debug("authenticating by Web Browser")
114
+
115
+ # TODO: switch to the new AuthHttpServer class instead of doing this manually
116
+ socket_connection = self._socket(socket.AF_INET, socket.SOCK_STREAM)
117
+
118
+ if os.getenv("SNOWFLAKE_AUTH_SOCKET_REUSE_PORT", "False").lower() == "true":
119
+ if IS_WINDOWS:
120
+ logger.warning(
121
+ "Configuration SNOWFLAKE_AUTH_SOCKET_REUSE_PORT is not available in Windows. Ignoring."
122
+ )
123
+ else:
124
+ socket_connection.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
125
+
126
+ try:
127
+ hostname = os.getenv("SF_AUTH_SOCKET_ADDR", "localhost")
128
+ try:
129
+ socket_connection.bind(
130
+ (
131
+ hostname,
132
+ int(os.getenv("SF_AUTH_SOCKET_PORT", 0)),
133
+ )
134
+ )
135
+ except socket.gaierror as ex:
136
+ if ex.args[0] == socket.EAI_NONAME:
137
+ raise OperationalError(
138
+ msg=f"{hostname} is not found. Ensure /etc/hosts has "
139
+ f"{hostname} entry.",
140
+ errno=ER_NO_HOSTNAME_FOUND,
141
+ )
142
+ else:
143
+ raise ex
144
+ socket_connection.listen(0) # no backlog
145
+ callback_port = socket_connection.getsockname()[1]
146
+
147
+ if conn._disable_console_login:
148
+ logger.debug("step 1: query GS to obtain SSO url")
149
+ sso_url = self._get_sso_url(
150
+ conn, authenticator, service_name, account, callback_port, user
151
+ )
152
+ else:
153
+ logger.debug("step 1: constructing console login url")
154
+ sso_url = self._get_console_login_url(conn, callback_port, user)
155
+
156
+ logger.debug("Validate SSO URL")
157
+ if not is_valid_url(sso_url):
158
+ self._handle_failure(
159
+ conn=conn,
160
+ ret={
161
+ "code": ER_INVALID_VALUE,
162
+ "message": (f"The SSO URL provided {sso_url} is invalid"),
163
+ },
164
+ )
165
+ return
166
+
167
+ print(
168
+ "Initiating login request with your identity provider. A "
169
+ "browser window should have opened for you to complete the "
170
+ "login. If you can't see it, check existing browser windows, "
171
+ "or your OS settings. Press CTRL+C to abort and try again..."
172
+ )
173
+
174
+ logger.debug("step 2: open a browser")
175
+ print(f"Going to open: {sso_url} to authenticate...")
176
+ if not self._webbrowser.open_new(sso_url):
177
+ print(
178
+ "We were unable to open a browser window for you, "
179
+ "please open the url above manually then paste the "
180
+ "URL you are redirected to into the terminal."
181
+ )
182
+ url = input("Enter the URL the SSO URL redirected you to: ")
183
+ self._process_get_url(url)
184
+ if not self._token:
185
+ # Input contained no token, either URL was incorrectly pasted,
186
+ # empty or just wrong
187
+ self._handle_failure(
188
+ conn=conn,
189
+ ret={
190
+ "code": ER_UNABLE_TO_OPEN_BROWSER,
191
+ "message": (
192
+ "Unable to open a browser in this environment and "
193
+ "SSO URL contained no token"
194
+ ),
195
+ },
196
+ )
197
+ return
198
+ else:
199
+ logger.debug("step 3: accept SAML token")
200
+ self._receive_saml_token(conn, socket_connection)
201
+ finally:
202
+ socket_connection.close()
203
+
204
+ def reauthenticate(
205
+ self,
206
+ *,
207
+ conn: SnowflakeConnection,
208
+ **kwargs: Any,
209
+ ) -> dict[str, bool]:
210
+ conn.authenticate_with_retry(self)
211
+ return {"success": True}
212
+
213
+ def _receive_saml_token(self, conn: SnowflakeConnection, socket_connection) -> None:
214
+ """Receives SAML token from web browser."""
215
+ while True:
216
+ try:
217
+ attempts = 0
218
+ raw_data = bytearray()
219
+ socket_client = None
220
+ max_attempts = 15
221
+
222
+ msg_dont_wait = (
223
+ os.getenv("SNOWFLAKE_AUTH_SOCKET_MSG_DONTWAIT", "false").lower()
224
+ == "true"
225
+ )
226
+ if IS_WINDOWS:
227
+ if msg_dont_wait:
228
+ logger.warning(
229
+ "Configuration SNOWFLAKE_AUTH_SOCKET_MSG_DONTWAIT is not available in Windows. Ignoring."
230
+ )
231
+ msg_dont_wait = False
232
+
233
+ # when running in a containerized environment, socket_client.recv ocassionally returns an empty byte array
234
+ # an immediate successive call to socket_client.recv gets the actual data
235
+ while len(raw_data) == 0 and attempts < max_attempts:
236
+ attempts += 1
237
+ read_sockets, _write_sockets, _exception_sockets = select.select(
238
+ [socket_connection], [], []
239
+ )
240
+
241
+ if read_sockets[0] is not None:
242
+ # Receive the data in small chunks and retransmit it
243
+ socket_client, _ = socket_connection.accept()
244
+
245
+ try:
246
+ if msg_dont_wait:
247
+ # WSL containerized environment sometimes causes socket_client.recv to hang indefinetly
248
+ # To avoid this, passing the socket.MSG_DONTWAIT flag which raises BlockingIOError if
249
+ # operation would block
250
+ logger.debug(
251
+ "Calling socket_client.recv with MSG_DONTWAIT flag due to SNOWFLAKE_AUTH_SOCKET_MSG_DONTWAIT env var"
252
+ )
253
+ raw_data = socket_client.recv(
254
+ BUF_SIZE, socket.MSG_DONTWAIT
255
+ )
256
+ else:
257
+ raw_data = socket_client.recv(BUF_SIZE)
258
+
259
+ except BlockingIOError:
260
+ logger.debug(
261
+ "BlockingIOError raised from socket.recv while attempting to retrieve callback token request"
262
+ )
263
+ if attempts < max_attempts:
264
+ sleep_time = 0.25
265
+ logger.debug(
266
+ f"Waiting {sleep_time} seconds before trying again"
267
+ )
268
+ time.sleep(sleep_time)
269
+ else:
270
+ logger.debug("Exceeded retry count")
271
+
272
+ data = raw_data.decode("utf-8").split("\r\n")
273
+
274
+ if not self._process_options(data, socket_client):
275
+ self._process_receive_saml_token(conn, data, socket_client)
276
+ break
277
+
278
+ finally:
279
+ socket_client.shutdown(socket.SHUT_RDWR)
280
+ socket_client.close()
281
+
282
+ def _process_options(self, data: list[str], socket_client: socket.socket) -> bool:
283
+ """Allows JS Ajax access to this endpoint."""
284
+ for line in data:
285
+ if line.startswith("OPTIONS "):
286
+ break
287
+ else:
288
+ return False
289
+
290
+ self._get_user_agent(data)
291
+ requested_headers, requested_origin = self._check_post_requested(data)
292
+ if not requested_headers:
293
+ return False
294
+
295
+ if not self._validate_origin(requested_origin):
296
+ # validate Origin and fail if not match with the server.
297
+ return False
298
+
299
+ self._origin = requested_origin
300
+ content = [
301
+ "HTTP/1.1 200 OK",
302
+ "Date: {}".format(
303
+ time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime())
304
+ ),
305
+ "Access-Control-Allow-Methods: POST, GET",
306
+ f"Access-Control-Allow-Headers: {requested_headers}",
307
+ "Access-Control-Max-Age: 86400",
308
+ f"Access-Control-Allow-Origin: {self._origin}",
309
+ "",
310
+ "",
311
+ ]
312
+ socket_client.sendall("\r\n".join(content).encode("utf-8"))
313
+ return True
314
+
315
+ def _validate_origin(self, requested_origin: str) -> bool:
316
+ ret = urlsplit(requested_origin)
317
+ netloc = ret.netloc.split(":")
318
+ host_got = netloc[0]
319
+ port_got = (
320
+ netloc[1] if len(netloc) > 1 else (443 if self._protocol == "https" else 80)
321
+ )
322
+
323
+ return (
324
+ ret.scheme == self._protocol
325
+ and host_got == self._host
326
+ and port_got == self._port
327
+ )
328
+
329
+ def _process_receive_saml_token(
330
+ self, conn: SnowflakeConnection, data: list[str], socket_client: socket.socket
331
+ ) -> None:
332
+ if not self._process_get(data) and not self._process_post(conn, data):
333
+ return # error
334
+
335
+ content = [
336
+ "HTTP/1.1 200 OK",
337
+ "Content-Type: text/html",
338
+ ]
339
+ if self._origin:
340
+ data = {"consent": self.consent_cache_id_token}
341
+ msg = json.dumps(data)
342
+ content.append(f"Access-Control-Allow-Origin: {self._origin}")
343
+ content.append("Vary: Accept-Encoding, Origin")
344
+ else:
345
+ msg = f"""
346
+ <!DOCTYPE html><html><head><meta charset="UTF-8"/>
347
+ <link rel="icon" href="data:,">
348
+ <title>SAML Response for Snowflake</title></head>
349
+ <body>
350
+ Your identity was confirmed and propagated to Snowflake {self._application}.
351
+ You can close this window now and go back where you started from.
352
+ </body></html>"""
353
+ content.append(f"Content-Length: {len(msg)}")
354
+ content.append("")
355
+ content.append(msg)
356
+
357
+ socket_client.sendall("\r\n".join(content).encode("utf-8"))
358
+
359
+ def _check_post_requested(self, data: list[str]) -> tuple[str | None, str | None]:
360
+ request_line = None
361
+ header_line = None
362
+ origin_line = None
363
+ for line in data:
364
+ if line.startswith("Access-Control-Request-Method:"):
365
+ request_line = line
366
+ elif line.startswith("Access-Control-Request-Headers:"):
367
+ header_line = line
368
+ elif line.startswith("Origin:"):
369
+ origin_line = line
370
+
371
+ if (
372
+ not request_line
373
+ or not header_line
374
+ or not origin_line
375
+ or request_line.split(":")[1].strip() != "POST"
376
+ ):
377
+ return None, None
378
+
379
+ return (
380
+ header_line.split(":")[1].strip(),
381
+ ":".join(origin_line.split(":")[1:]).strip(),
382
+ )
383
+
384
+ def _process_get_url(self, url: str) -> None:
385
+ parsed = parse_qs(urlparse(url).query)
386
+ if "token" not in parsed or not parsed["token"][0]:
387
+ return
388
+ self._token = parsed["token"][0]
389
+
390
+ def _process_get(self, data: list[str]) -> bool:
391
+ for line in data:
392
+ if line.startswith("GET "):
393
+ target_line = line
394
+ break
395
+ else:
396
+ return False
397
+
398
+ self._get_user_agent(data)
399
+ _, url, _ = target_line.split()
400
+ self._process_get_url(url)
401
+ return True
402
+
403
+ def _process_post(self, conn: SnowflakeConnection, data: list[str]) -> bool:
404
+ for line in data:
405
+ if line.startswith("POST "):
406
+ break
407
+ else:
408
+ self._handle_failure(
409
+ conn=conn,
410
+ ret={
411
+ "code": ER_IDP_CONNECTION_ERROR,
412
+ "message": "Invalid HTTP request from web browser. Idp "
413
+ "authentication could have failed.",
414
+ },
415
+ )
416
+ return False
417
+
418
+ self._get_user_agent(data)
419
+ try:
420
+ # parse the response as JSON
421
+ payload = json.loads(data[-1])
422
+ self._token = payload.get("token")
423
+ self.consent_cache_id_token = payload.get("consent", True)
424
+ except Exception:
425
+ # key=value form.
426
+ self._token = parse_qs(data[-1])["token"][0]
427
+ return True
428
+
429
+ def _get_user_agent(self, data: list[str]) -> None:
430
+ for line in data:
431
+ if line.lower().startswith("user-agent"):
432
+ logger.debug(line)
433
+ break
434
+ else:
435
+ logger.debug("No User-Agent")
436
+
437
+ def _get_sso_url(
438
+ self,
439
+ conn: SnowflakeConnection,
440
+ authenticator: str,
441
+ service_name: str | None,
442
+ account: str,
443
+ callback_port: int,
444
+ user: str,
445
+ ) -> str:
446
+ """Gets SSO URL from Snowflake."""
447
+ headers = {
448
+ HTTP_HEADER_CONTENT_TYPE: CONTENT_TYPE_APPLICATION_JSON,
449
+ HTTP_HEADER_ACCEPT: CONTENT_TYPE_APPLICATION_JSON,
450
+ HTTP_HEADER_USER_AGENT: PYTHON_CONNECTOR_USER_AGENT,
451
+ }
452
+ if service_name:
453
+ headers[HTTP_HEADER_SERVICE_NAME] = service_name
454
+
455
+ url = "/session/authenticator-request"
456
+ body = Auth.base_auth_data(
457
+ user,
458
+ account,
459
+ conn._rest._connection.application,
460
+ conn._rest._connection._internal_application_name,
461
+ conn._rest._connection._internal_application_version,
462
+ conn._rest._connection._ocsp_mode(),
463
+ conn._rest._connection.login_timeout,
464
+ conn._rest._connection._network_timeout,
465
+ )
466
+
467
+ body["data"]["AUTHENTICATOR"] = authenticator
468
+ body["data"]["BROWSER_MODE_REDIRECT_PORT"] = str(callback_port)
469
+ logger.debug(
470
+ "account=%s, authenticator=%s, user=%s", account, authenticator, user
471
+ )
472
+ ret = conn._rest._post_request(
473
+ url,
474
+ headers,
475
+ json.dumps(body),
476
+ timeout=conn._rest._connection.login_timeout,
477
+ socket_timeout=conn._rest._connection.login_timeout,
478
+ )
479
+ if not ret["success"]:
480
+ self._handle_failure(conn=conn, ret=ret)
481
+ data = ret["data"]
482
+ sso_url = data["ssoUrl"]
483
+ self._proof_key = data["proofKey"]
484
+ return sso_url
485
+
486
+ def _get_console_login_url(
487
+ self, conn: SnowflakeConnection, port: int, user: str
488
+ ) -> str:
489
+ self._proof_key = base64.b64encode(secrets.token_bytes(32)).decode("ascii")
490
+ url = (
491
+ conn._rest.server_url
492
+ + "/console/login?"
493
+ + urlencode(
494
+ {
495
+ "login_name": user,
496
+ "browser_mode_redirect_port": port,
497
+ "proof_key": self._proof_key,
498
+ }
499
+ )
500
+ )
501
+ logger.debug(f"Console Log In URL: {url}")
502
+ return url
@@ -0,0 +1,94 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import typing
5
+ from enum import Enum, unique
6
+
7
+ from ..network import WORKLOAD_IDENTITY_AUTHENTICATOR
8
+ from ..wif_util import (
9
+ AttestationProvider,
10
+ WorkloadIdentityAttestation,
11
+ create_attestation,
12
+ )
13
+ from .by_plugin import AuthByPlugin, AuthType
14
+
15
+
16
+ @unique
17
+ class ApiFederatedAuthenticationType(Enum):
18
+ """An API-specific enum of the WIF authentication type."""
19
+
20
+ AWS = "AWS"
21
+ AZURE = "AZURE"
22
+ GCP = "GCP"
23
+ OIDC = "OIDC"
24
+
25
+ @staticmethod
26
+ def from_attestation(
27
+ attestation: WorkloadIdentityAttestation,
28
+ ) -> ApiFederatedAuthenticationType:
29
+ """Maps the internal / driver-specific attestation providers to API authenticator types.
30
+
31
+ The AttestationProvider is related to how the driver fetches the credential, while the API authenticator
32
+ type is related to how the credential is verified. In most current cases these may be the same, though
33
+ in the future we could have, for example, multiple AttestationProviders that all fetch an OIDC ID token.
34
+ """
35
+ if attestation.provider == AttestationProvider.AWS:
36
+ return ApiFederatedAuthenticationType.AWS
37
+ if attestation.provider == AttestationProvider.AZURE:
38
+ return ApiFederatedAuthenticationType.AZURE
39
+ if attestation.provider == AttestationProvider.GCP:
40
+ return ApiFederatedAuthenticationType.GCP
41
+ if attestation.provider == AttestationProvider.OIDC:
42
+ return ApiFederatedAuthenticationType.OIDC
43
+ raise ValueError(f"Unknown attestation provider '{attestation.provider}'")
44
+
45
+
46
+ class AuthByWorkloadIdentity(AuthByPlugin):
47
+ """Plugin to authenticate via workload identity."""
48
+
49
+ def __init__(
50
+ self,
51
+ *,
52
+ provider: AttestationProvider | None = None,
53
+ token: str | None = None,
54
+ entra_resource: str | None = None,
55
+ **kwargs,
56
+ ) -> None:
57
+ super().__init__(**kwargs)
58
+ self.provider = provider
59
+ self.token = token
60
+ self.entra_resource = entra_resource
61
+
62
+ self.attestation: WorkloadIdentityAttestation | None = None
63
+
64
+ def type_(self) -> AuthType:
65
+ return AuthType.WORKLOAD_IDENTITY
66
+
67
+ def reset_secrets(self) -> None:
68
+ self.attestation = None
69
+
70
+ def update_body(self, body: dict[typing.Any, typing.Any]) -> None:
71
+ body["data"]["AUTHENTICATOR"] = WORKLOAD_IDENTITY_AUTHENTICATOR
72
+ body["data"]["PROVIDER"] = ApiFederatedAuthenticationType.from_attestation(
73
+ self.attestation
74
+ ).value
75
+ body["data"]["TOKEN"] = self.attestation.credential
76
+
77
+ def prepare(self, **kwargs: typing.Any) -> None:
78
+ """Fetch the token."""
79
+ self.attestation = create_attestation(
80
+ self.provider, self.entra_resource, self.token
81
+ )
82
+
83
+ def reauthenticate(self, **kwargs: typing.Any) -> dict[str, bool]:
84
+ """This is only relevant for AuthByIdToken, which uses a web-browser based flow. All other auth plugins just call authenticate() again."""
85
+ return {"success": False}
86
+
87
+ @property
88
+ def assertion_content(self) -> str:
89
+ """Returns the CSP provider name and an identifier. Used for logging purposes."""
90
+ if not self.attestation:
91
+ return ""
92
+ properties = self.attestation.user_identifier_components
93
+ properties["_provider"] = self.attestation.provider.value
94
+ return json.dumps(properties, sort_keys=True, separators=(",", ":"))