snowflake-connector-python 3.16.0__cp313-cp313-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- snowflake/connector/__init__.py +96 -0
- snowflake/connector/_query_context_cache.py +276 -0
- snowflake/connector/_sql_util.py +48 -0
- snowflake/connector/_utils.py +70 -0
- snowflake/connector/arrow_context.py +203 -0
- snowflake/connector/auth/__init__.py +53 -0
- snowflake/connector/auth/_auth.py +586 -0
- snowflake/connector/auth/_http_server.py +220 -0
- snowflake/connector/auth/_oauth_base.py +400 -0
- snowflake/connector/auth/by_plugin.py +219 -0
- snowflake/connector/auth/default.py +36 -0
- snowflake/connector/auth/idtoken.py +72 -0
- snowflake/connector/auth/keypair.py +222 -0
- snowflake/connector/auth/no_auth.py +39 -0
- snowflake/connector/auth/oauth.py +49 -0
- snowflake/connector/auth/oauth_code.py +479 -0
- snowflake/connector/auth/oauth_credentials.py +66 -0
- snowflake/connector/auth/okta.py +335 -0
- snowflake/connector/auth/pat.py +39 -0
- snowflake/connector/auth/usrpwdmfa.py +69 -0
- snowflake/connector/auth/webbrowser.py +502 -0
- snowflake/connector/auth/workload_identity.py +94 -0
- snowflake/connector/azure_storage_client.py +276 -0
- snowflake/connector/backoff_policies.py +141 -0
- snowflake/connector/bind_upload_agent.py +89 -0
- snowflake/connector/cache.py +696 -0
- snowflake/connector/compat.py +127 -0
- snowflake/connector/config_manager.py +496 -0
- snowflake/connector/connection.py +2273 -0
- snowflake/connector/connection_diagnostic.py +776 -0
- snowflake/connector/constants.py +442 -0
- snowflake/connector/converter.py +785 -0
- snowflake/connector/converter_issue23517.py +87 -0
- snowflake/connector/converter_null.py +14 -0
- snowflake/connector/converter_snowsql.py +205 -0
- snowflake/connector/cursor.py +1951 -0
- snowflake/connector/dbapi.py +53 -0
- snowflake/connector/description.py +19 -0
- snowflake/connector/direct_file_operation_utils.py +88 -0
- snowflake/connector/encryption_util.py +220 -0
- snowflake/connector/errorcode.py +91 -0
- snowflake/connector/errors.py +615 -0
- snowflake/connector/externals_utils/__init__.py +0 -0
- snowflake/connector/externals_utils/externals_setup.py +27 -0
- snowflake/connector/feature.py +4 -0
- snowflake/connector/file_compression_type.py +118 -0
- snowflake/connector/file_lock.py +72 -0
- snowflake/connector/file_transfer_agent.py +1215 -0
- snowflake/connector/file_util.py +153 -0
- snowflake/connector/gcs_storage_client.py +474 -0
- snowflake/connector/gzip_decoder.py +85 -0
- snowflake/connector/local_storage_client.py +90 -0
- snowflake/connector/log_configuration.py +60 -0
- snowflake/connector/logging_utils/__init__.py +0 -0
- snowflake/connector/logging_utils/filters.py +72 -0
- snowflake/connector/nanoarrow_arrow_iterator.cp313-win_amd64.pyd +0 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/ArrayConverter.cpp +60 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/ArrayConverter.hpp +29 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/BinaryConverter.cpp +19 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/BinaryConverter.hpp +26 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/BooleanConverter.cpp +21 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/BooleanConverter.hpp +23 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/CArrowChunkIterator.cpp +557 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/CArrowChunkIterator.hpp +98 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/CArrowIterator.cpp +125 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/CArrowIterator.hpp +115 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/CArrowTableIterator.cpp +1005 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/CArrowTableIterator.hpp +127 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/DateConverter.cpp +47 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/DateConverter.hpp +46 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/DecFloatConverter.cpp +83 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/DecFloatConverter.hpp +35 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/DecimalConverter.cpp +97 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/DecimalConverter.hpp +72 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/FixedSizeListConverter.cpp +73 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/FixedSizeListConverter.hpp +28 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/FloatConverter.cpp +30 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/FloatConverter.hpp +35 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/IColumnConverter.hpp +17 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/IntConverter.cpp +23 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/IntConverter.hpp +45 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/IntervalConverter.cpp +71 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/IntervalConverter.hpp +56 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/LICENSE.txt +209 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/MapConverter.cpp +75 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/MapConverter.hpp +30 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/ObjectConverter.cpp +46 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/ObjectConverter.hpp +29 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/Python/Common.cpp +8 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/Python/Common.hpp +95 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/Python/Helpers.cpp +57 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/Python/Helpers.hpp +36 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/SnowflakeType.cpp +34 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/SnowflakeType.hpp +48 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/StringConverter.cpp +19 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/StringConverter.hpp +26 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/TimeConverter.cpp +36 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/TimeConverter.hpp +31 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/TimeStampConverter.cpp +346 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/TimeStampConverter.hpp +145 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/Util/macros.hpp +14 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/Util/time.cpp +65 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/Util/time.hpp +68 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_accessors.h +101 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_alloc.h +127 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_assert.h +45 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_builder.h +1908 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_emitter.h +215 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_endian.h +125 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_epilogue.h +7 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_flatbuffers.h +55 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_identifier.h +148 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_iov.h +31 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_prologue.h +8 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_refmap.h +144 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_rtconfig.h +162 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_types.h +97 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/flatcc_verifier.h +239 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/flatcc_portable.h +14 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/paligned_alloc.h +210 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pattributes.h +84 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pdiagnostic.h +84 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pdiagnostic_pop.h +20 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pdiagnostic_push.h +51 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pendian.h +206 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pendian_detect.h +118 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pinline.h +19 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pinttypes.h +52 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/portable.h +2 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/portable_basic.h +25 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pstatic_assert.h +67 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pstdalign.h +162 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pstdint.h +898 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/punaligned.h +190 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pversion.h +6 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc/portable/pwarnings.h +52 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/flatcc.c +3204 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow.c +3217 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow.h +3618 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow.hpp +379 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow_arrow_iterator.pyx +256 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow_device.c +512 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow_device.h +350 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow_ipc.c +33273 -0
- snowflake/connector/nanoarrow_cpp/ArrowIterator/nanoarrow_ipc.h +397 -0
- snowflake/connector/nanoarrow_cpp/Logging/logging.cpp +100 -0
- snowflake/connector/nanoarrow_cpp/Logging/logging.hpp +49 -0
- snowflake/connector/network.py +1297 -0
- snowflake/connector/ocsp_asn1crypto.py +447 -0
- snowflake/connector/ocsp_snowflake.py +1883 -0
- snowflake/connector/options.py +131 -0
- snowflake/connector/pandas_tools.py +732 -0
- snowflake/connector/proxy.py +43 -0
- snowflake/connector/py.typed +0 -0
- snowflake/connector/result_batch.py +786 -0
- snowflake/connector/result_set.py +319 -0
- snowflake/connector/s3_storage_client.py +605 -0
- snowflake/connector/secret_detector.py +181 -0
- snowflake/connector/sf_dirs.py +64 -0
- snowflake/connector/sfbinaryformat.py +35 -0
- snowflake/connector/sfdatetime.py +345 -0
- snowflake/connector/snow_logging.py +144 -0
- snowflake/connector/sqlstate.py +9 -0
- snowflake/connector/ssd_internal_keys.py +30 -0
- snowflake/connector/ssl_wrap_socket.py +134 -0
- snowflake/connector/storage_client.py +478 -0
- snowflake/connector/telemetry.py +249 -0
- snowflake/connector/telemetry_oob.py +543 -0
- snowflake/connector/test_util.py +30 -0
- snowflake/connector/time_util.py +159 -0
- snowflake/connector/token_cache.py +402 -0
- snowflake/connector/tool/__init__.py +0 -0
- snowflake/connector/tool/dump_certs.py +57 -0
- snowflake/connector/tool/dump_ocsp_response.py +139 -0
- snowflake/connector/tool/dump_ocsp_response_cache.py +194 -0
- snowflake/connector/tool/probe_connection.py +69 -0
- snowflake/connector/url_util.py +49 -0
- snowflake/connector/util_text.py +301 -0
- snowflake/connector/vendored/__init__.py +3 -0
- snowflake/connector/vendored/requests/LICENSE +175 -0
- snowflake/connector/vendored/requests/__init__.py +169 -0
- snowflake/connector/vendored/requests/__version__.py +14 -0
- snowflake/connector/vendored/requests/_internal_utils.py +50 -0
- snowflake/connector/vendored/requests/adapters.py +537 -0
- snowflake/connector/vendored/requests/api.py +157 -0
- snowflake/connector/vendored/requests/auth.py +315 -0
- snowflake/connector/vendored/requests/certs.py +17 -0
- snowflake/connector/vendored/requests/compat.py +79 -0
- snowflake/connector/vendored/requests/cookies.py +561 -0
- snowflake/connector/vendored/requests/exceptions.py +140 -0
- snowflake/connector/vendored/requests/help.py +134 -0
- snowflake/connector/vendored/requests/hooks.py +33 -0
- snowflake/connector/vendored/requests/models.py +1033 -0
- snowflake/connector/vendored/requests/sessions.py +833 -0
- snowflake/connector/vendored/requests/status_codes.py +128 -0
- snowflake/connector/vendored/requests/structures.py +99 -0
- snowflake/connector/vendored/requests/utils.py +1093 -0
- snowflake/connector/vendored/urllib3/LICENSE.txt +21 -0
- snowflake/connector/vendored/urllib3/__init__.py +85 -0
- snowflake/connector/vendored/urllib3/_collections.py +355 -0
- snowflake/connector/vendored/urllib3/_version.py +2 -0
- snowflake/connector/vendored/urllib3/connection.py +572 -0
- snowflake/connector/vendored/urllib3/connectionpool.py +1137 -0
- snowflake/connector/vendored/urllib3/contrib/__init__.py +0 -0
- snowflake/connector/vendored/urllib3/contrib/_appengine_environ.py +36 -0
- snowflake/connector/vendored/urllib3/contrib/_securetransport/__init__.py +0 -0
- snowflake/connector/vendored/urllib3/contrib/_securetransport/bindings.py +519 -0
- snowflake/connector/vendored/urllib3/contrib/_securetransport/low_level.py +397 -0
- snowflake/connector/vendored/urllib3/contrib/appengine.py +314 -0
- snowflake/connector/vendored/urllib3/contrib/ntlmpool.py +130 -0
- snowflake/connector/vendored/urllib3/contrib/pyopenssl.py +509 -0
- snowflake/connector/vendored/urllib3/contrib/securetransport.py +920 -0
- snowflake/connector/vendored/urllib3/contrib/socks.py +216 -0
- snowflake/connector/vendored/urllib3/exceptions.py +323 -0
- snowflake/connector/vendored/urllib3/fields.py +274 -0
- snowflake/connector/vendored/urllib3/filepost.py +98 -0
- snowflake/connector/vendored/urllib3/packages/__init__.py +0 -0
- snowflake/connector/vendored/urllib3/packages/backports/__init__.py +0 -0
- snowflake/connector/vendored/urllib3/packages/backports/makefile.py +51 -0
- snowflake/connector/vendored/urllib3/packages/backports/weakref_finalize.py +155 -0
- snowflake/connector/vendored/urllib3/packages/six.py +1076 -0
- snowflake/connector/vendored/urllib3/poolmanager.py +540 -0
- snowflake/connector/vendored/urllib3/request.py +191 -0
- snowflake/connector/vendored/urllib3/response.py +885 -0
- snowflake/connector/vendored/urllib3/util/__init__.py +49 -0
- snowflake/connector/vendored/urllib3/util/connection.py +156 -0
- snowflake/connector/vendored/urllib3/util/proxy.py +57 -0
- snowflake/connector/vendored/urllib3/util/queue.py +22 -0
- snowflake/connector/vendored/urllib3/util/request.py +146 -0
- snowflake/connector/vendored/urllib3/util/response.py +107 -0
- snowflake/connector/vendored/urllib3/util/retry.py +620 -0
- snowflake/connector/vendored/urllib3/util/ssl_.py +495 -0
- snowflake/connector/vendored/urllib3/util/ssl_match_hostname.py +159 -0
- snowflake/connector/vendored/urllib3/util/ssltransport.py +221 -0
- snowflake/connector/vendored/urllib3/util/timeout.py +271 -0
- snowflake/connector/vendored/urllib3/util/url.py +435 -0
- snowflake/connector/vendored/urllib3/util/wait.py +152 -0
- snowflake/connector/version.py +3 -0
- snowflake/connector/wif_util.py +407 -0
- snowflake_connector_python-3.16.0.dist-info/METADATA +1475 -0
- snowflake_connector_python-3.16.0.dist-info/RECORD +246 -0
- snowflake_connector_python-3.16.0.dist-info/WHEEL +5 -0
- snowflake_connector_python-3.16.0.dist-info/entry_points.txt +4 -0
- snowflake_connector_python-3.16.0.dist-info/licenses/LICENSE.txt +202 -0
- snowflake_connector_python-3.16.0.dist-info/licenses/NOTICE +8 -0
- snowflake_connector_python-3.16.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,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
|