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