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,220 @@
1
+ #
2
+ # Copyright (c) 2012-2023 Snowflake Computing Inc. All rights reserved.
3
+ #
4
+
5
+ from __future__ import annotations
6
+
7
+ import logging
8
+ import os
9
+ import select
10
+ import socket
11
+ import time
12
+ import urllib.parse
13
+ from collections.abc import Callable
14
+ from types import TracebackType
15
+
16
+ from typing_extensions import Self
17
+
18
+ from ..compat import IS_WINDOWS
19
+
20
+ logger = logging.getLogger(__name__)
21
+
22
+
23
+ def _use_msg_dont_wait() -> bool:
24
+ if os.getenv("SNOWFLAKE_AUTH_SOCKET_MSG_DONTWAIT", "false").lower() != "true":
25
+ return False
26
+ if IS_WINDOWS:
27
+ logger.warning(
28
+ "Configuration SNOWFLAKE_AUTH_SOCKET_MSG_DONTWAIT is not available in Windows. Ignoring."
29
+ )
30
+ return False
31
+ return True
32
+
33
+
34
+ def _wrap_socket_recv() -> Callable[[socket.socket, int], bytes]:
35
+ dont_wait = _use_msg_dont_wait()
36
+ if dont_wait:
37
+ # WSL containerized environment sometimes causes socket_client.recv to hang indefinetly
38
+ # To avoid this, passing the socket.MSG_DONTWAIT flag which raises BlockingIOError if
39
+ # operation would block
40
+ logger.debug(
41
+ "Will call socket.recv with MSG_DONTWAIT flag due to SNOWFLAKE_AUTH_SOCKET_MSG_DONTWAIT env var"
42
+ )
43
+ socket_recv = (
44
+ (lambda sock, buf_size: socket.socket.recv(sock, buf_size, socket.MSG_DONTWAIT))
45
+ if dont_wait
46
+ else (lambda sock, buf_size: socket.socket.recv(sock, buf_size))
47
+ )
48
+
49
+ def socket_recv_checked(sock: socket.socket, buf_size: int) -> bytes:
50
+ raw = socket_recv(sock, buf_size)
51
+ # when running in a containerized environment, socket_client.recv occasionally returns an empty byte array
52
+ # an immediate successive call to socket_client.recv gets the actual data
53
+ if len(raw) == 0:
54
+ raw = socket_recv(sock, buf_size)
55
+ return raw
56
+
57
+ return socket_recv_checked
58
+
59
+
60
+ class AuthHttpServer:
61
+ """Simple HTTP server to receive callbacks through for auth purposes."""
62
+
63
+ DEFAULT_MAX_ATTEMPTS = 15
64
+ DEFAULT_TIMEOUT = 30.0
65
+
66
+ PORT_BIND_MAX_ATTEMPTS = 10
67
+ PORT_BIND_TIMEOUT = 20.0
68
+
69
+ def __init__(
70
+ self,
71
+ uri: str,
72
+ buf_size: int = 16384,
73
+ ) -> None:
74
+ parsed_uri = urllib.parse.urlparse(uri)
75
+ self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
76
+ self.buf_size = buf_size
77
+ if os.getenv("SNOWFLAKE_AUTH_SOCKET_REUSE_PORT", "False").lower() == "true":
78
+ if IS_WINDOWS:
79
+ logger.warning(
80
+ "Configuration SNOWFLAKE_AUTH_SOCKET_REUSE_PORT is not available in Windows. Ignoring."
81
+ )
82
+ else:
83
+ self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
84
+
85
+ port = parsed_uri.port or 0
86
+ for attempt in range(1, self.DEFAULT_MAX_ATTEMPTS + 1):
87
+ try:
88
+ self._socket.bind(
89
+ (
90
+ parsed_uri.hostname,
91
+ port,
92
+ )
93
+ )
94
+ break
95
+ except socket.gaierror as ex:
96
+ logger.error(
97
+ f"Failed to bind authorization callback server to port {port}: {ex}"
98
+ )
99
+ raise
100
+ except OSError as ex:
101
+ if attempt == self.DEFAULT_MAX_ATTEMPTS:
102
+ logger.error(
103
+ f"Failed to bind authorization callback server to port {port}: {ex}"
104
+ )
105
+ raise
106
+ logger.warning(
107
+ f"Attempt {attempt}/{self.DEFAULT_MAX_ATTEMPTS}. "
108
+ f"Failed to bind authorization callback server to port {port}: {ex}"
109
+ )
110
+ time.sleep(self.PORT_BIND_TIMEOUT / self.PORT_BIND_MAX_ATTEMPTS)
111
+ try:
112
+ self._socket.listen(0) # no backlog
113
+ except Exception as ex:
114
+ logger.error(f"Failed to start listening for auth callback: {ex}")
115
+ self.close()
116
+ raise
117
+ port = self._socket.getsockname()[1]
118
+ self._uri = urllib.parse.ParseResult(
119
+ scheme=parsed_uri.scheme,
120
+ netloc=parsed_uri.hostname + ":" + str(port),
121
+ path=parsed_uri.path,
122
+ params=parsed_uri.params,
123
+ query=parsed_uri.query,
124
+ fragment=parsed_uri.fragment,
125
+ )
126
+
127
+ @property
128
+ def url(self) -> str:
129
+ return self._uri.geturl()
130
+
131
+ @property
132
+ def port(self) -> int:
133
+ return self._uri.port
134
+
135
+ @property
136
+ def hostname(self) -> str:
137
+ return self._uri.hostname
138
+
139
+ def _try_poll(
140
+ self, attempts: int, attempt_timeout: float | None
141
+ ) -> (socket.socket | None, int):
142
+ for attempt in range(attempts):
143
+ read_sockets = select.select([self._socket], [], [], attempt_timeout)[0]
144
+ if read_sockets and read_sockets[0] is not None:
145
+ return self._socket.accept()[0], attempt
146
+ return None, attempts
147
+
148
+ def _try_receive_block(
149
+ self, client_socket: socket.socket, attempts: int, attempt_timeout: float | None
150
+ ) -> bytes | None:
151
+ if attempt_timeout is not None:
152
+ client_socket.settimeout(attempt_timeout)
153
+ recv = _wrap_socket_recv()
154
+ for attempt in range(attempts):
155
+ try:
156
+ return recv(client_socket, self.buf_size)
157
+ except BlockingIOError:
158
+ if attempt < attempts - 1:
159
+ cooldown = min(attempt_timeout, 0.25) if attempt_timeout else 0.25
160
+ logger.debug(
161
+ f"BlockingIOError raised from socket.recv on {1 + attempt}/{attempts} attempt."
162
+ f"Waiting for {cooldown} seconds before trying again"
163
+ )
164
+ time.sleep(cooldown)
165
+ except socket.timeout:
166
+ logger.debug(
167
+ f"socket.recv timed out on {1 + attempt}/{attempts} attempt."
168
+ )
169
+ return None
170
+
171
+ def receive_block(
172
+ self,
173
+ max_attempts: int = None,
174
+ timeout: float | int | None = None,
175
+ ) -> (list[str] | None, socket.socket | None):
176
+ if max_attempts is None:
177
+ max_attempts = self.DEFAULT_MAX_ATTEMPTS
178
+ if timeout is None:
179
+ timeout = self.DEFAULT_TIMEOUT
180
+ """Receive a message with a maximum attempt count and a timeout in seconds, blocking."""
181
+ if not self._socket:
182
+ raise RuntimeError(
183
+ "Operation is not supported, server was already shut down."
184
+ )
185
+ attempt_timeout = timeout / max_attempts if timeout else None
186
+ client_socket, poll_attempts = self._try_poll(max_attempts, attempt_timeout)
187
+ if client_socket is None:
188
+ return None, None
189
+ raw_block = self._try_receive_block(
190
+ client_socket, max_attempts - poll_attempts, attempt_timeout
191
+ )
192
+ if raw_block:
193
+ return raw_block.decode("utf-8").split("\r\n"), client_socket
194
+ try:
195
+ client_socket.shutdown(socket.SHUT_RDWR)
196
+ except OSError:
197
+ pass
198
+ client_socket.close()
199
+ return None, None
200
+
201
+ def close(self) -> None:
202
+ """Closes the underlying socket.
203
+ After having close() being called the server object cannot be reused.
204
+ """
205
+ if self._socket:
206
+ self._socket.close()
207
+ self._socket = None
208
+
209
+ def __enter__(self) -> Self:
210
+ """Context manager."""
211
+ return self
212
+
213
+ def __exit__(
214
+ self,
215
+ exc_type: type[BaseException] | None,
216
+ exc_val: BaseException | None,
217
+ exc_tb: TracebackType | None,
218
+ ) -> None:
219
+ """Context manager with disposing underlying networking objects."""
220
+ self.close()
@@ -0,0 +1,400 @@
1
+ #
2
+ # Copyright (c) 2012-2023 Snowflake Computing Inc. All rights reserved.
3
+ #
4
+
5
+ from __future__ import annotations
6
+
7
+ import base64
8
+ import json
9
+ import logging
10
+ import urllib.parse
11
+ from abc import ABC, abstractmethod
12
+ from typing import TYPE_CHECKING, Any
13
+ from urllib.error import HTTPError, URLError
14
+
15
+ from ..errorcode import (
16
+ ER_FAILED_TO_REQUEST,
17
+ ER_IDP_CONNECTION_ERROR,
18
+ ER_NO_CLIENT_ID,
19
+ ER_NO_CLIENT_SECRET,
20
+ )
21
+ from ..errors import Error, ProgrammingError
22
+ from ..network import OAUTH_AUTHENTICATOR
23
+ from ..secret_detector import SecretDetector
24
+ from ..token_cache import TokenCache, TokenKey, TokenType
25
+ from ..vendored import urllib3
26
+ from .by_plugin import AuthByPlugin, AuthType
27
+
28
+ if TYPE_CHECKING:
29
+ from .. import SnowflakeConnection
30
+
31
+ logger = logging.getLogger(__name__)
32
+
33
+
34
+ class _OAuthTokensMixin:
35
+ def __init__(
36
+ self,
37
+ token_cache: TokenCache | None,
38
+ refresh_token_enabled: bool,
39
+ idp_host: str,
40
+ ) -> None:
41
+ self._access_token = None
42
+ self._refresh_token_enabled = refresh_token_enabled
43
+ if self._refresh_token_enabled:
44
+ self._refresh_token = None
45
+ self._token_cache = token_cache
46
+ if self._token_cache:
47
+ logger.debug("token cache is going to be used if needed")
48
+ self._idp_host = idp_host
49
+ self._access_token_key: TokenKey | None = None
50
+ if self._refresh_token_enabled:
51
+ self._refresh_token_key: TokenKey | None = None
52
+
53
+ def _update_cache_keys(self, user: str) -> None:
54
+ if self._token_cache:
55
+ self._user = user
56
+
57
+ def _get_access_token_cache_key(self) -> TokenKey | None:
58
+ return (
59
+ TokenKey(self._user, self._idp_host, TokenType.OAUTH_ACCESS_TOKEN)
60
+ if self._token_cache and self._user
61
+ else None
62
+ )
63
+
64
+ def _get_refresh_token_cache_key(self) -> TokenKey | None:
65
+ return (
66
+ TokenKey(self._user, self._idp_host, TokenType.OAUTH_REFRESH_TOKEN)
67
+ if self._refresh_token_enabled and self._token_cache and self._user
68
+ else None
69
+ )
70
+
71
+ def _pop_cached_token(self, key: TokenKey | None) -> str | None:
72
+ if self._token_cache is None or key is None:
73
+ return None
74
+ return self._token_cache.retrieve(key)
75
+
76
+ def _pop_cached_access_token(self) -> bool:
77
+ """Retrieves OAuth access token from the token cache if enabled"""
78
+ self._access_token = self._pop_cached_token(self._get_access_token_cache_key())
79
+ return self._access_token is not None
80
+
81
+ def _pop_cached_refresh_token(self) -> bool:
82
+ """Retrieves OAuth refresh token from the token cache if enabled"""
83
+ if self._refresh_token_enabled:
84
+ self._refresh_token = self._pop_cached_token(
85
+ self._get_refresh_token_cache_key()
86
+ )
87
+ return self._refresh_token is not None
88
+ return False
89
+
90
+ def _reset_cached_token(self, key: TokenKey | None, token: str | None) -> None:
91
+ if self._token_cache is None or key is None:
92
+ return
93
+ if token:
94
+ self._token_cache.store(key, token)
95
+ else:
96
+ self._token_cache.remove(key)
97
+
98
+ def _reset_access_token(self, access_token: str | None = None) -> None:
99
+ """Updates OAuth access token both in memory and in the token cache if enabled"""
100
+ logger.debug(
101
+ "resetting access token to %s",
102
+ "*" * len(access_token) if access_token else None,
103
+ )
104
+ self._access_token = access_token
105
+ self._reset_cached_token(self._get_access_token_cache_key(), self._access_token)
106
+
107
+ def _reset_refresh_token(self, refresh_token: str | None = None) -> None:
108
+ """Updates OAuth refresh token both in memory and in the token cache if necessary"""
109
+ if self._refresh_token_enabled:
110
+ logger.debug(
111
+ "resetting refresh token to %s",
112
+ "*" * len(refresh_token) if refresh_token else None,
113
+ )
114
+ self._refresh_token = refresh_token
115
+ self._reset_cached_token(
116
+ self._get_refresh_token_cache_key(), self._refresh_token
117
+ )
118
+
119
+ def _reset_temporary_state(self) -> None:
120
+ self._access_token = None
121
+ if self._refresh_token_enabled:
122
+ self._refresh_token = None
123
+ if self._token_cache:
124
+ self._user = None
125
+
126
+
127
+ class AuthByOAuthBase(AuthByPlugin, _OAuthTokensMixin, ABC):
128
+ """A base abstract class for OAuth authenticators"""
129
+
130
+ def __init__(
131
+ self,
132
+ client_id: str,
133
+ client_secret: str,
134
+ token_request_url: str,
135
+ scope: str,
136
+ token_cache: TokenCache | None,
137
+ refresh_token_enabled: bool,
138
+ **kwargs,
139
+ ) -> None:
140
+ super().__init__(**kwargs)
141
+ _OAuthTokensMixin.__init__(
142
+ self,
143
+ token_cache=token_cache,
144
+ refresh_token_enabled=refresh_token_enabled,
145
+ idp_host=urllib.parse.urlparse(token_request_url).hostname,
146
+ )
147
+ self._client_id = client_id
148
+ self._client_secret = client_secret
149
+ self._token_request_url = token_request_url
150
+ self._scope = scope
151
+ if refresh_token_enabled:
152
+ logger.debug("oauth refresh token is going to be used if needed")
153
+ self._scope += (" " if self._scope else "") + "offline_access"
154
+
155
+ @abstractmethod
156
+ def _request_tokens(
157
+ self,
158
+ *,
159
+ conn: SnowflakeConnection,
160
+ authenticator: str,
161
+ service_name: str | None,
162
+ account: str,
163
+ user: str,
164
+ password: str | None,
165
+ **kwargs: Any,
166
+ ) -> (str | None, str | None):
167
+ """Request new access and optionally refresh tokens from IdP.
168
+
169
+ This function should implement specific tokens querying flow.
170
+ """
171
+ raise NotImplementedError
172
+
173
+ @abstractmethod
174
+ def _get_oauth_type_id(self) -> str:
175
+ """Get OAuth specific authenticator id to be passed to Snowflake.
176
+
177
+ This function should return a unique OAuth authenticator id.
178
+ """
179
+ raise NotImplementedError
180
+
181
+ def reset_secrets(self) -> None:
182
+ logger.debug("resetting secrets")
183
+ self._reset_temporary_state()
184
+
185
+ @property
186
+ def type_(self) -> AuthType:
187
+ return AuthType.OAUTH
188
+
189
+ @property
190
+ def assertion_content(self) -> str:
191
+ """Returns the token."""
192
+ return self._access_token or ""
193
+
194
+ @staticmethod
195
+ def _validate_client_credentials_present(
196
+ client_id: str, client_secret: str, connection: SnowflakeConnection
197
+ ) -> tuple[str, str]:
198
+ if client_id is None or client_id == "":
199
+ Error.errorhandler_wrapper(
200
+ connection,
201
+ None,
202
+ ProgrammingError,
203
+ {
204
+ "msg": "Oauth code flow requirement 'client_id' is empty",
205
+ "errno": ER_NO_CLIENT_ID,
206
+ },
207
+ )
208
+ if client_secret is None or client_secret == "":
209
+ Error.errorhandler_wrapper(
210
+ connection,
211
+ None,
212
+ ProgrammingError,
213
+ {
214
+ "msg": "Oauth code flow requirement 'client_secret' is empty",
215
+ "errno": ER_NO_CLIENT_SECRET,
216
+ },
217
+ )
218
+
219
+ return client_id, client_secret
220
+
221
+ def reauthenticate(
222
+ self,
223
+ *,
224
+ conn: SnowflakeConnection,
225
+ **kwargs: Any,
226
+ ) -> dict[str, bool]:
227
+ self._reset_access_token()
228
+ if self._pop_cached_refresh_token():
229
+ logger.debug(
230
+ "OAuth refresh token is available, try to use it and get a new access token"
231
+ )
232
+ self._do_refresh_token(conn=conn)
233
+ conn.authenticate_with_retry(self)
234
+ return {"success": True}
235
+
236
+ def prepare(
237
+ self,
238
+ *,
239
+ conn: SnowflakeConnection,
240
+ authenticator: str,
241
+ service_name: str | None,
242
+ account: str,
243
+ user: str,
244
+ **kwargs: Any,
245
+ ) -> None:
246
+ """Web Browser based Authentication."""
247
+ logger.debug("authenticating with OAuth authorization code flow")
248
+ self._update_cache_keys(user=user)
249
+ if self._pop_cached_access_token():
250
+ logger.info(
251
+ "OAuth access token is already available in cache, no need to authenticate."
252
+ )
253
+ return
254
+ access_token, refresh_token = self._request_tokens(
255
+ conn=conn,
256
+ authenticator=authenticator,
257
+ service_name=service_name,
258
+ account=account,
259
+ user=user,
260
+ **kwargs,
261
+ )
262
+ self._reset_access_token(access_token)
263
+ self._reset_refresh_token(refresh_token)
264
+
265
+ def update_body(self, body: dict[Any, Any]) -> None:
266
+ """Used by Auth to update the request that gets sent to /v1/login-request.
267
+
268
+ Args:
269
+ body: existing request dictionary
270
+ """
271
+ body["data"]["AUTHENTICATOR"] = OAUTH_AUTHENTICATOR
272
+ body["data"]["TOKEN"] = self._access_token
273
+ body["data"]["OAUTH_TYPE"] = self._get_oauth_type_id()
274
+
275
+ def _do_refresh_token(self, conn: SnowflakeConnection) -> None:
276
+ """If a refresh token is available exchanges it with a new access token.
277
+ Updates self as a side-effect. Needs at lest self._refresh_token and client_id set.
278
+ """
279
+ if not self._refresh_token_enabled:
280
+ logger.debug("refresh_token feature is disabled")
281
+ return
282
+
283
+ resp = self._get_refresh_token_response(conn)
284
+ if not resp:
285
+ logger.info(
286
+ "failed to exchange the refresh token on a new OAuth access token"
287
+ )
288
+ self._reset_refresh_token()
289
+ return
290
+
291
+ try:
292
+ json_resp = json.loads(resp.data.decode())
293
+ self._reset_access_token(json_resp["access_token"])
294
+ if "refresh_token" in json_resp:
295
+ self._reset_refresh_token(json_resp["refresh_token"])
296
+ except (
297
+ json.JSONDecodeError,
298
+ KeyError,
299
+ ):
300
+ logger.error(
301
+ "refresh token exchange response did not contain 'access_token'"
302
+ )
303
+ logger.debug(
304
+ "received the following response body when exchanging refresh token: %s",
305
+ SecretDetector.mask_secrets(str(resp.data)),
306
+ )
307
+ self._reset_refresh_token()
308
+
309
+ def _get_refresh_token_response(
310
+ self, conn: SnowflakeConnection
311
+ ) -> urllib3.BaseHTTPResponse | None:
312
+ fields = {
313
+ "grant_type": "refresh_token",
314
+ "refresh_token": self._refresh_token,
315
+ }
316
+ if self._scope:
317
+ fields["scope"] = self._scope
318
+ try:
319
+ return urllib3.PoolManager().request_encode_body(
320
+ # TODO: use network pool to gain use of proxy settings and so on
321
+ "POST",
322
+ self._token_request_url,
323
+ encode_multipart=False,
324
+ headers=self._create_token_request_headers(),
325
+ fields=fields,
326
+ )
327
+ except HTTPError as e:
328
+ self._handle_failure(
329
+ conn=conn,
330
+ ret={
331
+ "code": ER_FAILED_TO_REQUEST,
332
+ "message": f"Failed to request new OAuth access token with a refresh token,"
333
+ f" url={e.url}, code={e.code}, reason={e.reason}",
334
+ },
335
+ )
336
+ except URLError as e:
337
+ self._handle_failure(
338
+ conn=conn,
339
+ ret={
340
+ "code": ER_FAILED_TO_REQUEST,
341
+ "message": f"Failed to request new OAuth access token with a refresh token, reason: {e.reason}",
342
+ },
343
+ )
344
+ except Exception:
345
+ self._handle_failure(
346
+ conn=conn,
347
+ ret={
348
+ "code": ER_FAILED_TO_REQUEST,
349
+ "message": "Failed to request new OAuth access token with a refresh token by unknown reason",
350
+ },
351
+ )
352
+ return None
353
+
354
+ def _get_request_token_response(
355
+ self,
356
+ connection: SnowflakeConnection,
357
+ fields: dict[str, str],
358
+ ) -> (str | None, str | None):
359
+ resp = urllib3.PoolManager().request_encode_body(
360
+ # TODO: use network pool to gain use of proxy settings and so on
361
+ "POST",
362
+ self._token_request_url,
363
+ headers=self._create_token_request_headers(),
364
+ encode_multipart=False,
365
+ fields=fields,
366
+ )
367
+ try:
368
+ logger.debug("OAuth IdP response received, try to parse it")
369
+ json_resp: dict = json.loads(resp.data)
370
+ access_token = json_resp["access_token"]
371
+ refresh_token = json_resp.get("refresh_token")
372
+ return access_token, refresh_token
373
+ except (
374
+ json.JSONDecodeError,
375
+ KeyError,
376
+ ):
377
+ logger.error("oauth response invalid, does not contain 'access_token'")
378
+ logger.debug(
379
+ "received the following response body when requesting oauth token: %s",
380
+ SecretDetector.mask_secrets(str(resp.data)),
381
+ )
382
+ self._handle_failure(
383
+ conn=connection,
384
+ ret={
385
+ "code": ER_IDP_CONNECTION_ERROR,
386
+ "message": "Invalid HTTP request from web browser. Idp "
387
+ "authentication could have failed.",
388
+ },
389
+ )
390
+ return None, None
391
+
392
+ def _create_token_request_headers(self) -> dict[str, str]:
393
+ return {
394
+ "Authorization": "Basic "
395
+ + base64.b64encode(
396
+ f"{self._client_id}:{self._client_secret}".encode()
397
+ ).decode(),
398
+ "Accept": "application/json",
399
+ "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
400
+ }