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,407 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import logging
5
+ import os
6
+ from base64 import b64encode
7
+ from dataclasses import dataclass
8
+ from enum import Enum, unique
9
+
10
+ import boto3
11
+ import jwt
12
+ from botocore.auth import SigV4Auth
13
+ from botocore.awsrequest import AWSRequest
14
+ from botocore.utils import InstanceMetadataRegionFetcher
15
+
16
+ from .errorcode import ER_WIF_CREDENTIALS_NOT_FOUND
17
+ from .errors import ProgrammingError
18
+ from .vendored import requests
19
+ from .vendored.requests import Response
20
+
21
+ logger = logging.getLogger(__name__)
22
+ SNOWFLAKE_AUDIENCE = "snowflakecomputing.com"
23
+ DEFAULT_ENTRA_SNOWFLAKE_RESOURCE = "api://fd3f753b-eed3-462c-b6a7-a4b5bb650aad"
24
+
25
+ """
26
+ References:
27
+ - https://learn.microsoft.com/en-us/entra/identity-platform/authentication-national-cloud#microsoft-entra-authentication-endpoints
28
+ - https://learn.microsoft.com/en-us/answers/questions/1190472/what-are-the-token-issuers-for-the-sovereign-cloud
29
+ """
30
+ AZURE_ISSUER_PREFIXES = [
31
+ "https://sts.windows.net/", # Public and USGov (v1 issuer)
32
+ "https://sts.chinacloudapi.cn/", # Mooncake (v1 issuer)
33
+ "https://login.microsoftonline.com/", # Public (v2 issuer)
34
+ "https://login.microsoftonline.us/", # USGov (v2 issuer)
35
+ "https://login.partner.microsoftonline.cn/", # Mooncake (v2 issuer)
36
+ ]
37
+
38
+
39
+ @unique
40
+ class AttestationProvider(Enum):
41
+ """A WIF provider implementation that can produce an attestation."""
42
+
43
+ AWS = "AWS"
44
+ """Provider that builds an encoded pre-signed GetCallerIdentity request using the current workload's IAM role."""
45
+ AZURE = "AZURE"
46
+ """Provider that requests an OAuth access token for the workload's managed identity."""
47
+ GCP = "GCP"
48
+ """Provider that requests an ID token for the workload's attached service account."""
49
+ OIDC = "OIDC"
50
+ """Provider that looks for an OIDC ID token."""
51
+
52
+ @staticmethod
53
+ def from_string(provider: str) -> AttestationProvider:
54
+ """Converts a string to a strongly-typed enum value of AttestationProvider."""
55
+ return AttestationProvider[provider.upper()]
56
+
57
+
58
+ @dataclass
59
+ class WorkloadIdentityAttestation:
60
+ provider: AttestationProvider
61
+ credential: str
62
+ user_identifier_components: dict
63
+
64
+
65
+ def try_metadata_service_call(
66
+ method: str, url: str, headers: dict, timeout_sec: int = 3
67
+ ) -> Response | None:
68
+ """Tries to make a HTTP request to the metadata service with the given URL, method, headers and timeout.
69
+
70
+ If we receive an error response or any exceptions are raised, returns None. Otherwise returns the response.
71
+ """
72
+ try:
73
+ res: Response = requests.request(
74
+ method=method, url=url, headers=headers, timeout=timeout_sec
75
+ )
76
+ if not res.ok:
77
+ return None
78
+ except requests.RequestException:
79
+ return None
80
+ return res
81
+
82
+
83
+ def extract_iss_and_sub_without_signature_verification(jwt_str: str) -> tuple[str, str]:
84
+ """Extracts the 'iss' and 'sub' claims from the given JWT, without verifying the signature.
85
+
86
+ Note: the real token verification (including signature verification) happens on the Snowflake side. The driver doesn't have
87
+ the keys to verify these JWTs, and in any case that's not where the security boundary is drawn.
88
+
89
+ We only decode the JWT here to get some basic claims, which will be used for a) a quick smoke test to ensure we got the right
90
+ issuer, and b) to find the unique user being asserted and populate assertion_content. The latter may be used for logging
91
+ and possibly caching.
92
+
93
+ If there are any errors in parsing the token or extracting iss and sub, this will return (None, None).
94
+ """
95
+ try:
96
+ claims = jwt.decode(jwt_str, options={"verify_signature": False})
97
+ except jwt.exceptions.InvalidTokenError:
98
+ logger.warning("Token is not a valid JWT.", exc_info=True)
99
+ return None, None
100
+
101
+ if not ("iss" in claims and "sub" in claims):
102
+ logger.warning("Token is missing 'iss' or 'sub' claims.")
103
+ return None, None
104
+
105
+ return claims["iss"], claims["sub"]
106
+
107
+
108
+ def get_aws_region() -> str | None:
109
+ """Get the current AWS workload's region, if any."""
110
+ if "AWS_REGION" in os.environ: # Lambda
111
+ return os.environ["AWS_REGION"]
112
+ else: # EC2
113
+ return InstanceMetadataRegionFetcher().retrieve_region()
114
+
115
+
116
+ def get_aws_arn() -> str | None:
117
+ """Get the current AWS workload's ARN, if any."""
118
+ caller_identity = boto3.client("sts").get_caller_identity()
119
+ if not caller_identity or "Arn" not in caller_identity:
120
+ return None
121
+ return caller_identity["Arn"]
122
+
123
+
124
+ def get_aws_partition(arn: str) -> str | None:
125
+ """Get the current AWS partition from ARN, if any.
126
+
127
+ Args:
128
+ arn (str): The Amazon Resource Name (ARN) string.
129
+
130
+ Returns:
131
+ str | None: The AWS partition (e.g., 'aws', 'aws-cn', 'aws-us-gov')
132
+ if found, otherwise None.
133
+
134
+ Reference: https://docs.aws.amazon.com/IAM/latest/UserGuide/reference-arns.html.
135
+ """
136
+ if not arn or not isinstance(arn, str):
137
+ return None
138
+ parts = arn.split(":")
139
+ if len(parts) > 1 and parts[0] == "arn" and parts[1]:
140
+ return parts[1]
141
+ logger.warning("Invalid AWS ARN: %s", arn)
142
+ return None
143
+
144
+
145
+ def get_aws_sts_hostname(region: str, partition: str) -> str | None:
146
+ """Constructs the AWS STS hostname for a given region and partition.
147
+
148
+ Args:
149
+ region (str): The AWS region (e.g., 'us-east-1', 'cn-north-1').
150
+ partition (str): The AWS partition (e.g., 'aws', 'aws-cn', 'aws-us-gov').
151
+
152
+ Returns:
153
+ str | None: The AWS STS hostname (e.g., 'sts.us-east-1.amazonaws.com')
154
+ if a valid hostname can be constructed, otherwise None.
155
+
156
+ References:
157
+ - https://docs.aws.amazon.com/sdkref/latest/guide/feature-sts-regionalized-endpoints.html
158
+ - https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_region-endpoints.html
159
+ - https://docs.aws.amazon.com/general/latest/gr/sts.html
160
+ """
161
+ if (
162
+ not region
163
+ or not partition
164
+ or not isinstance(region, str)
165
+ or not isinstance(partition, str)
166
+ ):
167
+ return None
168
+
169
+ if partition == "aws":
170
+ # For the 'aws' partition, STS endpoints are generally regional
171
+ # except for the global endpoint (sts.amazonaws.com) which is
172
+ # generally resolved to us-east-1 under the hood by the SDKs
173
+ # when a region is not explicitly specified.
174
+ # However, for explicit regional endpoints, the format is sts.<region>.amazonaws.com
175
+ return f"sts.{region}.amazonaws.com"
176
+ elif partition == "aws-cn":
177
+ # China regions have a different domain suffix
178
+ return f"sts.{region}.amazonaws.com.cn"
179
+ elif partition == "aws-us-gov":
180
+ return (
181
+ f"sts.{region}.amazonaws.com" # GovCloud uses .com, but dedicated regions
182
+ )
183
+ else:
184
+ logger.warning("Invalid AWS partition: %s", partition)
185
+ return None
186
+
187
+
188
+ def create_aws_attestation() -> WorkloadIdentityAttestation | None:
189
+ """Tries to create a workload identity attestation for AWS.
190
+
191
+ If the application isn't running on AWS or no credentials were found, returns None.
192
+ """
193
+ aws_creds = boto3.session.Session().get_credentials()
194
+ if not aws_creds:
195
+ logger.debug("No AWS credentials were found.")
196
+ return None
197
+ region = get_aws_region()
198
+ if not region:
199
+ logger.debug("No AWS region was found.")
200
+ return None
201
+ arn = get_aws_arn()
202
+ if not arn:
203
+ logger.debug("No AWS caller identity was found.")
204
+ return None
205
+ partition = get_aws_partition(arn)
206
+ if not partition:
207
+ logger.debug("No AWS partition was found.")
208
+ return None
209
+
210
+ sts_hostname = get_aws_sts_hostname(region, partition)
211
+ request = AWSRequest(
212
+ method="POST",
213
+ url=f"https://{sts_hostname}/?Action=GetCallerIdentity&Version=2011-06-15",
214
+ headers={
215
+ "Host": sts_hostname,
216
+ "X-Snowflake-Audience": SNOWFLAKE_AUDIENCE,
217
+ },
218
+ )
219
+
220
+ SigV4Auth(aws_creds, "sts", region).add_auth(request)
221
+
222
+ assertion_dict = {
223
+ "url": request.url,
224
+ "method": request.method,
225
+ "headers": dict(request.headers.items()),
226
+ }
227
+ credential = b64encode(json.dumps(assertion_dict).encode("utf-8")).decode("utf-8")
228
+ return WorkloadIdentityAttestation(
229
+ AttestationProvider.AWS, credential, {"arn": arn}
230
+ )
231
+
232
+
233
+ def create_gcp_attestation() -> WorkloadIdentityAttestation | None:
234
+ """Tries to create a workload identity attestation for GCP.
235
+
236
+ If the application isn't running on GCP or no credentials were found, returns None.
237
+ """
238
+ res = try_metadata_service_call(
239
+ method="GET",
240
+ url=f"http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/identity?audience={SNOWFLAKE_AUDIENCE}",
241
+ headers={
242
+ "Metadata-Flavor": "Google",
243
+ },
244
+ )
245
+ if res is None:
246
+ # Most likely we're just not running on GCP, which may be expected.
247
+ logger.debug("GCP metadata server request was not successful.")
248
+ return None
249
+
250
+ jwt_str = res.content.decode("utf-8")
251
+ issuer, subject = extract_iss_and_sub_without_signature_verification(jwt_str)
252
+ if not issuer or not subject:
253
+ return None
254
+ if issuer != "https://accounts.google.com":
255
+ # This might happen if we're running on a different platform that responds to the same metadata request signature as GCP.
256
+ logger.debug("Unexpected GCP token issuer '%s'", issuer)
257
+ return None
258
+
259
+ return WorkloadIdentityAttestation(
260
+ AttestationProvider.GCP, jwt_str, {"sub": subject}
261
+ )
262
+
263
+
264
+ def create_azure_attestation(
265
+ snowflake_entra_resource: str,
266
+ ) -> WorkloadIdentityAttestation | None:
267
+ """Tries to create a workload identity attestation for Azure.
268
+
269
+ If the application isn't running on Azure or no credentials were found, returns None.
270
+ """
271
+ headers = {"Metadata": "True"}
272
+ url_without_query_string = "http://169.254.169.254/metadata/identity/oauth2/token"
273
+ query_params = f"api-version=2018-02-01&resource={snowflake_entra_resource}"
274
+
275
+ # Check if running in Azure Functions environment
276
+ identity_endpoint = os.environ.get("IDENTITY_ENDPOINT")
277
+ identity_header = os.environ.get("IDENTITY_HEADER")
278
+ is_azure_functions = identity_endpoint is not None
279
+
280
+ if is_azure_functions:
281
+ if not identity_header:
282
+ logger.warning("Managed identity is not enabled on this Azure function.")
283
+ return None
284
+
285
+ # Azure Functions uses a different endpoint, headers and API version.
286
+ url_without_query_string = identity_endpoint
287
+ headers = {"X-IDENTITY-HEADER": identity_header}
288
+ query_params = f"api-version=2019-08-01&resource={snowflake_entra_resource}"
289
+
290
+ # Some Azure Functions environments may require client_id in the URL
291
+ managed_identity_client_id = os.environ.get("MANAGED_IDENTITY_CLIENT_ID")
292
+ if managed_identity_client_id:
293
+ query_params += f"&client_id={managed_identity_client_id}"
294
+
295
+ res = try_metadata_service_call(
296
+ method="GET",
297
+ url=f"{url_without_query_string}?{query_params}",
298
+ headers=headers,
299
+ )
300
+ if res is None:
301
+ # Most likely we're just not running on Azure, which may be expected.
302
+ logger.debug("Azure metadata server request was not successful.")
303
+ return None
304
+
305
+ try:
306
+ jwt_str = res.json().get("access_token")
307
+ if not jwt_str:
308
+ # Could be that Managed Identity is disabled.
309
+ logger.debug("No access token found in Azure response.")
310
+ return None
311
+ except (ValueError, KeyError) as e:
312
+ logger.debug(f"Error parsing Azure response: {e}")
313
+ return None
314
+
315
+ issuer, subject = extract_iss_and_sub_without_signature_verification(jwt_str)
316
+ if not issuer or not subject:
317
+ return None
318
+ if not any(
319
+ issuer.startswith(issuer_prefix) for issuer_prefix in AZURE_ISSUER_PREFIXES
320
+ ):
321
+ # This might happen if we're running on a different platform that responds to the same metadata request signature as Azure.
322
+ logger.debug("Unexpected Azure token issuer '%s'", issuer)
323
+ return None
324
+
325
+ return WorkloadIdentityAttestation(
326
+ AttestationProvider.AZURE, jwt_str, {"iss": issuer, "sub": subject}
327
+ )
328
+
329
+
330
+ def create_oidc_attestation(token: str | None) -> WorkloadIdentityAttestation | None:
331
+ """Tries to create an attestation using the given token.
332
+
333
+ If this is not populated, returns None.
334
+ """
335
+ if not token:
336
+ logger.debug("No OIDC token was specified.")
337
+ return None
338
+
339
+ issuer, subject = extract_iss_and_sub_without_signature_verification(token)
340
+ if not issuer or not subject:
341
+ return None
342
+
343
+ return WorkloadIdentityAttestation(
344
+ AttestationProvider.OIDC, token, {"iss": issuer, "sub": subject}
345
+ )
346
+
347
+
348
+ def create_autodetect_attestation(
349
+ entra_resource: str, token: str | None = None
350
+ ) -> WorkloadIdentityAttestation | None:
351
+ """Tries to create an attestation using the auto-detected runtime environment.
352
+
353
+ If no attestation can be found, returns None.
354
+ """
355
+ attestation = create_oidc_attestation(token)
356
+ if attestation:
357
+ return attestation
358
+
359
+ attestation = create_aws_attestation()
360
+ if attestation:
361
+ return attestation
362
+
363
+ attestation = create_azure_attestation(entra_resource)
364
+ if attestation:
365
+ return attestation
366
+
367
+ attestation = create_gcp_attestation()
368
+ if attestation:
369
+ return attestation
370
+
371
+ return None
372
+
373
+
374
+ def create_attestation(
375
+ provider: AttestationProvider | None,
376
+ entra_resource: str | None = None,
377
+ token: str | None = None,
378
+ ) -> WorkloadIdentityAttestation:
379
+ """Entry point to create an attestation using the given provider.
380
+
381
+ If the provider is None, this will try to auto-detect a credential from the runtime environment. If the provider fails to detect a credential,
382
+ a ProgrammingError will be raised.
383
+
384
+ If an explicit entra_resource was provided to the connector, this will be used. Otherwise, the default Snowflake Entra resource will be used.
385
+ """
386
+ entra_resource = entra_resource or DEFAULT_ENTRA_SNOWFLAKE_RESOURCE
387
+
388
+ attestation: WorkloadIdentityAttestation = None
389
+ if provider == AttestationProvider.AWS:
390
+ attestation = create_aws_attestation()
391
+ elif provider == AttestationProvider.AZURE:
392
+ attestation = create_azure_attestation(entra_resource)
393
+ elif provider == AttestationProvider.GCP:
394
+ attestation = create_gcp_attestation()
395
+ elif provider == AttestationProvider.OIDC:
396
+ attestation = create_oidc_attestation(token)
397
+ elif provider is None:
398
+ attestation = create_autodetect_attestation(entra_resource, token)
399
+
400
+ if not attestation:
401
+ provider_str = "auto-detect" if provider is None else provider.value
402
+ raise ProgrammingError(
403
+ msg=f"No workload identity credential was found for '{provider_str}'.",
404
+ errno=ER_WIF_CREDENTIALS_NOT_FOUND,
405
+ )
406
+
407
+ return attestation