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,479 @@
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 hashlib
9
+ import json
10
+ import logging
11
+ import secrets
12
+ import socket
13
+ import time
14
+ import urllib.parse
15
+ import webbrowser
16
+ from typing import TYPE_CHECKING, Any
17
+
18
+ from ..compat import parse_qs, urlparse, urlsplit
19
+ from ..constants import OAUTH_TYPE_AUTHORIZATION_CODE
20
+ from ..errorcode import (
21
+ ER_INVALID_VALUE,
22
+ ER_OAUTH_CALLBACK_ERROR,
23
+ ER_OAUTH_SERVER_TIMEOUT,
24
+ ER_OAUTH_STATE_CHANGED,
25
+ ER_UNABLE_TO_OPEN_BROWSER,
26
+ )
27
+ from ..errors import Error, ProgrammingError
28
+ from ..token_cache import TokenCache
29
+ from ._http_server import AuthHttpServer
30
+ from ._oauth_base import AuthByOAuthBase
31
+
32
+ if TYPE_CHECKING:
33
+ from .. import SnowflakeConnection
34
+
35
+ logger = logging.getLogger(__name__)
36
+
37
+ BUF_SIZE = 16384
38
+
39
+
40
+ def _get_query_params(
41
+ url: str,
42
+ ) -> dict[str, list[str]]:
43
+ parsed = parse_qs(urlparse(url).query)
44
+ return parsed
45
+
46
+
47
+ class AuthByOauthCode(AuthByOAuthBase):
48
+ """Authenticates user by OAuth code flow."""
49
+
50
+ _LOCAL_APPLICATION_CLIENT_CREDENTIALS = "LOCAL_APPLICATION"
51
+
52
+ def __init__(
53
+ self,
54
+ application: str,
55
+ client_id: str,
56
+ client_secret: str,
57
+ authentication_url: str,
58
+ token_request_url: str,
59
+ redirect_uri: str,
60
+ scope: str,
61
+ host: str,
62
+ pkce_enabled: bool = True,
63
+ token_cache: TokenCache | None = None,
64
+ refresh_token_enabled: bool = False,
65
+ external_browser_timeout: int | None = None,
66
+ enable_single_use_refresh_tokens: bool = False,
67
+ connection: SnowflakeConnection | None = None,
68
+ **kwargs,
69
+ ) -> None:
70
+ authentication_url, redirect_uri = self._validate_oauth_code_uris(
71
+ authentication_url, redirect_uri, connection
72
+ )
73
+ client_id, client_secret = self._validate_client_credentials_with_defaults(
74
+ client_id,
75
+ client_secret,
76
+ authentication_url,
77
+ token_request_url,
78
+ host,
79
+ connection,
80
+ )
81
+
82
+ super().__init__(
83
+ client_id=client_id,
84
+ client_secret=client_secret,
85
+ token_request_url=token_request_url,
86
+ scope=scope,
87
+ token_cache=token_cache,
88
+ refresh_token_enabled=refresh_token_enabled,
89
+ **kwargs,
90
+ )
91
+ self._application = application
92
+ self._origin: str | None = None
93
+ self._authentication_url = authentication_url
94
+ self._redirect_uri = redirect_uri
95
+ self._state = secrets.token_urlsafe(43)
96
+ logger.debug("chose oauth state: %s", "".join("*" for _ in self._state))
97
+ self._protocol = "http"
98
+ self._pkce_enabled = pkce_enabled
99
+ if pkce_enabled:
100
+ logger.debug("oauth pkce is going to be used")
101
+ self._verifier: str | None = None
102
+ self._external_browser_timeout = external_browser_timeout
103
+ self._enable_single_use_refresh_tokens = enable_single_use_refresh_tokens
104
+
105
+ def _get_oauth_type_id(self) -> str:
106
+ return OAUTH_TYPE_AUTHORIZATION_CODE
107
+
108
+ def _request_tokens(
109
+ self,
110
+ *,
111
+ conn: SnowflakeConnection,
112
+ authenticator: str,
113
+ service_name: str | None,
114
+ account: str,
115
+ user: str,
116
+ **kwargs: Any,
117
+ ) -> (str | None, str | None):
118
+ """Web Browser based Authentication."""
119
+ logger.debug("authenticating with OAuth authorization code flow")
120
+ with AuthHttpServer(self._redirect_uri) as callback_server:
121
+ code = self._do_authorization_request(callback_server, conn)
122
+ return self._do_token_request(code, callback_server, conn)
123
+
124
+ def _check_post_requested(
125
+ self, data: list[str]
126
+ ) -> tuple[str, str] | tuple[None, None]:
127
+ request_line = None
128
+ header_line = None
129
+ origin_line = None
130
+ for line in data:
131
+ if line.startswith("Access-Control-Request-Method:"):
132
+ request_line = line
133
+ elif line.startswith("Access-Control-Request-Headers:"):
134
+ header_line = line
135
+ elif line.startswith("Origin:"):
136
+ origin_line = line
137
+
138
+ if (
139
+ not request_line
140
+ or not header_line
141
+ or not origin_line
142
+ or request_line.split(":")[1].strip() != "POST"
143
+ ):
144
+ return (None, None)
145
+
146
+ return (
147
+ header_line.split(":")[1].strip(),
148
+ ":".join(origin_line.split(":")[1:]).strip(),
149
+ )
150
+
151
+ def _process_options(
152
+ self, data: list[str], socket_client: socket.socket, hostname: str, port: int
153
+ ) -> bool:
154
+ """Allows JS Ajax access to this endpoint."""
155
+ for line in data:
156
+ if line.startswith("OPTIONS "):
157
+ break
158
+ else:
159
+ return False
160
+ requested_headers, requested_origin = self._check_post_requested(data)
161
+ if requested_headers is None or requested_origin is None:
162
+ return False
163
+
164
+ if not self._validate_origin(requested_origin, hostname, port):
165
+ # validate Origin and fail if not match with the server.
166
+ return False
167
+
168
+ self._origin = requested_origin
169
+ content = [
170
+ "HTTP/1.1 200 OK",
171
+ "Date: {}".format(
172
+ time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime())
173
+ ),
174
+ "Access-Control-Allow-Methods: POST, GET",
175
+ f"Access-Control-Allow-Headers: {requested_headers}",
176
+ "Access-Control-Max-Age: 86400",
177
+ f"Access-Control-Allow-Origin: {self._origin}",
178
+ "",
179
+ "",
180
+ ]
181
+ socket_client.sendall("\r\n".join(content).encode("utf-8"))
182
+ return True
183
+
184
+ def _validate_origin(self, requested_origin: str, hostname: str, port: int) -> bool:
185
+ ret = urlsplit(requested_origin)
186
+ netloc = ret.netloc.split(":")
187
+ host_got = netloc[0]
188
+ port_got = (
189
+ netloc[1] if len(netloc) > 1 else (443 if self._protocol == "https" else 80)
190
+ )
191
+
192
+ return (
193
+ ret.scheme == self._protocol and host_got == hostname and port_got == port
194
+ )
195
+
196
+ def _send_response(self, data: list[str], socket_client: socket.socket) -> None:
197
+ if not self._is_request_get(data):
198
+ return # error
199
+
200
+ response = [
201
+ "HTTP/1.1 200 OK",
202
+ "Content-Type: text/html",
203
+ ]
204
+ if self._origin:
205
+ msg = json.dumps({"consent": self.consent_cache_id_token})
206
+ response.append(f"Access-Control-Allow-Origin: {self._origin}")
207
+ response.append("Vary: Accept-Encoding, Origin")
208
+ else:
209
+ msg = f"""
210
+ <!DOCTYPE html><html><head><meta charset="UTF-8"/>
211
+ <link rel="icon" href="data:,">
212
+ <title>OAuth Response for Snowflake</title></head>
213
+ <body>
214
+ Your identity was confirmed and propagated to Snowflake {self._application}.
215
+ You can close this window now and go back where you started from.
216
+ </body></html>"""
217
+ response.append(f"Content-Length: {len(msg)}")
218
+ response.append("")
219
+ response.append(msg)
220
+
221
+ socket_client.sendall("\r\n".join(response).encode("utf-8"))
222
+
223
+ @staticmethod
224
+ def _has_code(url: str) -> bool:
225
+ return "code" in parse_qs(urlparse(url).query)
226
+
227
+ @staticmethod
228
+ def _is_request_get(data: list[str]) -> bool:
229
+ """Whether an HTTP request is a GET."""
230
+ return any(line.startswith("GET ") for line in data)
231
+
232
+ def _construct_authorization_request(self, redirect_uri: str) -> str:
233
+ params = {
234
+ "response_type": "code",
235
+ "client_id": self._client_id,
236
+ "redirect_uri": redirect_uri,
237
+ "state": self._state,
238
+ }
239
+ if self._scope:
240
+ params["scope"] = self._scope
241
+ if self._pkce_enabled:
242
+ self._verifier = secrets.token_urlsafe(43)
243
+ # calculate challenge and verifier
244
+ challenge = (
245
+ base64.urlsafe_b64encode(
246
+ hashlib.sha256(self._verifier.encode("utf-8")).digest()
247
+ )
248
+ .decode("utf-8")
249
+ .rstrip("=")
250
+ )
251
+ params["code_challenge"] = challenge
252
+ params["code_challenge_method"] = "S256"
253
+ url_params = urllib.parse.urlencode(params)
254
+ url = f"{self._authentication_url}?{url_params}"
255
+ return url
256
+
257
+ def _do_authorization_request(
258
+ self,
259
+ callback_server: AuthHttpServer,
260
+ connection: SnowflakeConnection,
261
+ ) -> str | None:
262
+ authorization_request = self._construct_authorization_request(
263
+ callback_server.url
264
+ )
265
+ logger.debug("step 1: going to open authorization URL")
266
+ print(
267
+ "Initiating login request with your identity provider. A "
268
+ "browser window should have opened for you to complete the "
269
+ "login. If you can't see it, check existing browser windows, "
270
+ "or your OS settings. Press CTRL+C to abort and try again..."
271
+ )
272
+ code, state = (
273
+ self._receive_authorization_callback(callback_server, connection)
274
+ if webbrowser.open(authorization_request)
275
+ else self._ask_authorization_callback_from_user(
276
+ authorization_request, connection
277
+ )
278
+ )
279
+ if not code:
280
+ self._handle_failure(
281
+ conn=connection,
282
+ ret={
283
+ "code": ER_UNABLE_TO_OPEN_BROWSER,
284
+ "message": (
285
+ "Unable to open a browser in this environment and "
286
+ "OAuth URL contained no authorization code."
287
+ ),
288
+ },
289
+ )
290
+ return None
291
+ if state != self._state:
292
+ self._handle_failure(
293
+ conn=connection,
294
+ ret={
295
+ "code": ER_OAUTH_STATE_CHANGED,
296
+ "message": "State changed during OAuth process.",
297
+ },
298
+ )
299
+ logger.debug(
300
+ "received oauth code: %s and state: %s",
301
+ "*" * len(code),
302
+ "*" * len(state),
303
+ )
304
+ return None
305
+ return code
306
+
307
+ def _do_token_request(
308
+ self,
309
+ code: str,
310
+ callback_server: AuthHttpServer,
311
+ connection: SnowflakeConnection,
312
+ ) -> (str | None, str | None):
313
+ logger.debug("step 2: received OAuth callback, requesting token")
314
+ fields = {
315
+ "grant_type": "authorization_code",
316
+ "code": code,
317
+ "redirect_uri": callback_server.url,
318
+ }
319
+ if self._enable_single_use_refresh_tokens:
320
+ fields["enable_single_use_refresh_tokens"] = "true"
321
+ if self._pkce_enabled:
322
+ assert self._verifier is not None
323
+ fields["code_verifier"] = self._verifier
324
+ return self._get_request_token_response(connection, fields)
325
+
326
+ def _receive_authorization_callback(
327
+ self,
328
+ http_server: AuthHttpServer,
329
+ connection: SnowflakeConnection,
330
+ ) -> (str | None, str | None):
331
+ logger.debug("trying to receive authorization redirected uri")
332
+ data, socket_connection = http_server.receive_block(
333
+ timeout=self._external_browser_timeout
334
+ )
335
+ if socket_connection is None:
336
+ self._handle_failure(
337
+ conn=connection,
338
+ ret={
339
+ "code": ER_OAUTH_SERVER_TIMEOUT,
340
+ "message": "Unable to receive the OAuth message within a given timeout. Please check the redirect URI and try again.",
341
+ },
342
+ )
343
+ return None, None
344
+ try:
345
+ if not self._process_options(
346
+ data, socket_connection, http_server.hostname, http_server.port
347
+ ):
348
+ self._send_response(data, socket_connection)
349
+ socket_connection.shutdown(socket.SHUT_RDWR)
350
+ except OSError:
351
+ pass
352
+ finally:
353
+ socket_connection.close()
354
+ return self._parse_authorization_redirected_request(
355
+ data[0].split(maxsplit=2)[1],
356
+ connection,
357
+ )
358
+
359
+ def _ask_authorization_callback_from_user(
360
+ self,
361
+ authorization_request: str,
362
+ connection: SnowflakeConnection,
363
+ ) -> (str | None, str | None):
364
+ logger.debug("requesting authorization redirected url from user")
365
+ print(
366
+ "We were unable to open a browser window for you, "
367
+ "please open the URL manually then paste the "
368
+ "URL you are redirected to into the terminal:\n"
369
+ f"{authorization_request}"
370
+ )
371
+ received_redirected_request = input(
372
+ "Enter the URL the OAuth flow redirected you to: "
373
+ )
374
+ code, state = self._parse_authorization_redirected_request(
375
+ received_redirected_request,
376
+ connection,
377
+ )
378
+ if not code:
379
+ self._handle_failure(
380
+ conn=connection,
381
+ ret={
382
+ "code": ER_UNABLE_TO_OPEN_BROWSER,
383
+ "message": (
384
+ "Unable to open a browser in this environment and "
385
+ "OAuth URL contained no code"
386
+ ),
387
+ },
388
+ )
389
+ return code, state
390
+
391
+ def _parse_authorization_redirected_request(
392
+ self,
393
+ url: str,
394
+ conn: SnowflakeConnection,
395
+ ) -> (str | None, str | None):
396
+ parsed = parse_qs(urlparse(url).query)
397
+ if "error" in parsed:
398
+ self._handle_failure(
399
+ conn=conn,
400
+ ret={
401
+ "code": ER_OAUTH_CALLBACK_ERROR,
402
+ "message": f"Oauth callback returned an {parsed['error'][0]} error{': ' + parsed['error_description'][0] if 'error_description' in parsed else '.'}",
403
+ },
404
+ )
405
+ return parsed.get("code", [None])[0], parsed.get("state", [None])[0]
406
+
407
+ @staticmethod
408
+ def _is_snowflake_as_idp(
409
+ authentication_url: str, token_request_url: str, host: str
410
+ ) -> bool:
411
+ return (authentication_url == "" or host in authentication_url) and (
412
+ token_request_url == "" or host in token_request_url
413
+ )
414
+
415
+ def _eligible_for_default_client_credentials(
416
+ self,
417
+ client_id: str,
418
+ client_secret: str,
419
+ authorization_url: str,
420
+ token_request_url: str,
421
+ host: str,
422
+ ) -> bool:
423
+ return (
424
+ (client_id == "" or client_secret is None)
425
+ and (client_secret == "" or client_secret is None)
426
+ and self.__class__._is_snowflake_as_idp(
427
+ authorization_url, token_request_url, host
428
+ )
429
+ )
430
+
431
+ def _validate_client_credentials_with_defaults(
432
+ self,
433
+ client_id: str,
434
+ client_secret: str,
435
+ authorization_url: str,
436
+ token_request_url: str,
437
+ host: str,
438
+ connection: SnowflakeConnection,
439
+ ) -> tuple[str, str] | None:
440
+ if self._eligible_for_default_client_credentials(
441
+ client_id, client_secret, authorization_url, token_request_url, host
442
+ ):
443
+ return (
444
+ self.__class__._LOCAL_APPLICATION_CLIENT_CREDENTIALS,
445
+ self.__class__._LOCAL_APPLICATION_CLIENT_CREDENTIALS,
446
+ )
447
+ else:
448
+ self._validate_client_credentials_present(
449
+ client_id, client_secret, connection
450
+ )
451
+ return client_id, client_secret
452
+
453
+ @staticmethod
454
+ def _validate_oauth_code_uris(
455
+ authorization_url: str, redirect_uri: str, connection: SnowflakeConnection
456
+ ) -> tuple[str, str]:
457
+ if authorization_url and not authorization_url.startswith("https://"):
458
+ Error.errorhandler_wrapper(
459
+ connection,
460
+ None,
461
+ ProgrammingError,
462
+ {
463
+ "msg": "OAuth supports only authorization urls that use 'https' scheme",
464
+ "errno": ER_INVALID_VALUE,
465
+ },
466
+ )
467
+ if redirect_uri and not (
468
+ redirect_uri.startswith("http://") or redirect_uri.startswith("https://")
469
+ ):
470
+ Error.errorhandler_wrapper(
471
+ connection,
472
+ None,
473
+ ProgrammingError,
474
+ {
475
+ "msg": "OAuth supports only authorization urls that use 'http(s)' scheme",
476
+ "errno": ER_INVALID_VALUE,
477
+ },
478
+ )
479
+ return authorization_url, redirect_uri
@@ -0,0 +1,66 @@
1
+ #
2
+ # Copyright (c) 2012-2023 Snowflake Computing Inc. All rights reserved.
3
+ #
4
+
5
+ from __future__ import annotations
6
+
7
+ import logging
8
+ from typing import TYPE_CHECKING, Any
9
+
10
+ from ..constants import OAUTH_TYPE_CLIENT_CREDENTIALS
11
+ from ..token_cache import TokenCache
12
+ from ._oauth_base import AuthByOAuthBase
13
+
14
+ if TYPE_CHECKING:
15
+ from .. import SnowflakeConnection
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+
20
+ class AuthByOauthCredentials(AuthByOAuthBase):
21
+ """Authenticates user by OAuth credentials - a client_id/client_secret pair."""
22
+
23
+ def __init__(
24
+ self,
25
+ application: str,
26
+ client_id: str,
27
+ client_secret: str,
28
+ token_request_url: str,
29
+ scope: str,
30
+ token_cache: TokenCache | None = None,
31
+ refresh_token_enabled: bool = False,
32
+ connection: SnowflakeConnection | None = None,
33
+ **kwargs,
34
+ ) -> None:
35
+ self._validate_client_credentials_present(client_id, client_secret, connection)
36
+ super().__init__(
37
+ client_id=client_id,
38
+ client_secret=client_secret,
39
+ token_request_url=token_request_url,
40
+ scope=scope,
41
+ token_cache=token_cache,
42
+ refresh_token_enabled=refresh_token_enabled,
43
+ **kwargs,
44
+ )
45
+ self._application = application
46
+ self._origin: str | None = None
47
+
48
+ def _get_oauth_type_id(self) -> str:
49
+ return OAUTH_TYPE_CLIENT_CREDENTIALS
50
+
51
+ def _request_tokens(
52
+ self,
53
+ *,
54
+ conn: SnowflakeConnection,
55
+ authenticator: str,
56
+ service_name: str | None,
57
+ account: str,
58
+ user: str,
59
+ **kwargs: Any,
60
+ ) -> (str | None, str | None):
61
+ logger.debug("authenticating with OAuth client credentials flow")
62
+ fields = {
63
+ "grant_type": "client_credentials",
64
+ "scope": self._scope,
65
+ }
66
+ return self._get_request_token_response(conn, fields)