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