Skip to content

Commit 6eb19e7

Browse files
sfc-gh-mkellersfc-gh-pczajka
authored andcommitted
SNOW-1825473 adding pat authentication integration (#2122)
1 parent 1aeacdb commit 6eb19e7

File tree

7 files changed

+63
-7
lines changed

7 files changed

+63
-7
lines changed

src/snowflake/connector/auth/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from .keypair import AuthByKeyPair
1212
from .oauth import AuthByOAuth
1313
from .okta import AuthByOkta
14+
from .pat import AuthByPAT
1415
from .usrpwdmfa import AuthByUsrPwdMfa
1516
from .webbrowser import AuthByWebBrowser
1617

@@ -23,13 +24,15 @@
2324
AuthByUsrPwdMfa,
2425
AuthByWebBrowser,
2526
AuthByIdToken,
27+
AuthByPAT,
2628
)
2729
)
2830

2931
__all__ = [
3032
"AuthByPlugin",
3133
"AuthByDefault",
3234
"AuthByKeyPair",
35+
"AuthByPAT",
3336
"AuthByOAuth",
3437
"AuthByOkta",
3538
"AuthByUsrPwdMfa",

src/snowflake/connector/auth/by_plugin.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ class AuthType(Enum):
5454
ID_TOKEN = "ID_TOKEN"
5555
USR_PWD_MFA = "USERNAME_PASSWORD_MFA"
5656
OKTA = "OKTA"
57+
PAT = "PROGRAMMATIC_ACCESS_TOKEN'"
5758

5859

5960
class AuthByPlugin(ABC):

src/snowflake/connector/auth/oauth.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ def type_(self) -> AuthType:
2222
return AuthType.OAUTH
2323

2424
@property
25-
def assertion_content(self) -> str:
25+
def assertion_content(self) -> str | None:
2626
"""Returns the token."""
2727
return self._oauth_token
2828

src/snowflake/connector/auth/pat.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
#
2+
# Copyright (c) 2012-2023 Snowflake Computing Inc. All rights reserved.
3+
#
4+
5+
from __future__ import annotations
6+
7+
import typing
8+
9+
from snowflake.connector.network import PROGRAMMATIC_ACCESS_TOKEN
10+
11+
from .by_plugin import AuthByPlugin, AuthType
12+
13+
14+
class AuthByPAT(AuthByPlugin):
15+
16+
def __init__(self, pat_token: str, **kwargs) -> None:
17+
super().__init__(**kwargs)
18+
self._pat_token: str | None = pat_token
19+
20+
def type_(self) -> AuthType:
21+
return AuthType.PAT
22+
23+
def reset_secrets(self) -> None:
24+
self._pat_token = None
25+
26+
def update_body(self, body: dict[typing.Any, typing.Any]) -> None:
27+
body["data"]["AUTHENTICATOR"] = PROGRAMMATIC_ACCESS_TOKEN
28+
body["data"]["TOKEN"] = self._pat_token
29+
30+
def prepare(
31+
self,
32+
**kwargs: typing.Any,
33+
) -> None:
34+
"""Nothing to do here, token should be obtained outside the driver."""
35+
pass
36+
37+
def reauthenticate(self, **kwargs: typing.Any) -> dict[str, bool]:
38+
return {"success": False}
39+
40+
@property
41+
def assertion_content(self) -> str | None:
42+
"""Returns the token."""
43+
return self._pat_token

src/snowflake/connector/connection.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
AuthByKeyPair,
4242
AuthByOAuth,
4343
AuthByOkta,
44+
AuthByPAT,
4445
AuthByPlugin,
4546
AuthByUsrPwdMfa,
4647
AuthByWebBrowser,
@@ -99,6 +100,7 @@
99100
EXTERNAL_BROWSER_AUTHENTICATOR,
100101
KEY_PAIR_AUTHENTICATOR,
101102
OAUTH_AUTHENTICATOR,
103+
PROGRAMMATIC_ACCESS_TOKEN,
102104
REQUEST_ID,
103105
USR_PWD_MFA_AUTHENTICATOR,
104106
ReauthenticationRequest,
@@ -185,7 +187,11 @@ def _get_private_bytes_from_file(
185187
"private_key": (None, (type(None), bytes, RSAPrivateKey)),
186188
"private_key_file": (None, (type(None), str)),
187189
"private_key_file_pwd": (None, (type(None), str, bytes)),
188-
"token": (None, (type(None), str)), # OAuth or JWT Token
190+
"token": (None, (type(None), str)), # OAuth/JWT/PAT Token
191+
"token_file_path": (
192+
None,
193+
(type(None), str, bytes),
194+
), # OAuth/JWT/PAT Token file path
189195
"authenticator": (DEFAULT_AUTHENTICATOR, (type(None), str)),
190196
"mfa_callback": (None, (type(None), Callable)),
191197
"password_callback": (None, (type(None), Callable)),
@@ -1115,6 +1121,8 @@ def __open_connection(self):
11151121
timeout=self.login_timeout,
11161122
backoff_generator=self._backoff_generator,
11171123
)
1124+
elif self._authenticator == PROGRAMMATIC_ACCESS_TOKEN:
1125+
self.auth_class = AuthByPAT(self._token)
11181126
else:
11191127
# okta URL, e.g., https://<account>.okta.com/
11201128
self.auth_class = AuthByOkta(
@@ -1263,11 +1271,12 @@ def __config(self, **kwargs):
12631271
if (
12641272
self.auth_class is None
12651273
and self._authenticator
1266-
not in [
1274+
not in (
12671275
EXTERNAL_BROWSER_AUTHENTICATOR,
12681276
OAUTH_AUTHENTICATOR,
12691277
KEY_PAIR_AUTHENTICATOR,
1270-
]
1278+
PROGRAMMATIC_ACCESS_TOKEN,
1279+
)
12711280
and not self._password
12721281
):
12731282
Error.errorhandler_wrapper(

src/snowflake/connector/errors.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
from logging import getLogger
1313
from typing import TYPE_CHECKING, Any
1414

15-
from .compat import BASE_EXCEPTION_CLASS
1615
from .secret_detector import SecretDetector
1716
from .telemetry import TelemetryData, TelemetryField
1817
from .time_util import get_time_millis
@@ -28,7 +27,7 @@
2827
RE_FORMATTED_ERROR = re.compile(r"^(\d{6,})(?: \((\S+)\))?:")
2928

3029

31-
class Error(BASE_EXCEPTION_CLASS):
30+
class Error(Exception):
3231
"""Base Snowflake exception class."""
3332

3433
def __init__(
@@ -369,7 +368,7 @@ def errorhandler_make_exception(
369368
return error_class(error_value)
370369

371370

372-
class _Warning(BASE_EXCEPTION_CLASS):
371+
class _Warning(Exception):
373372
"""Exception for important warnings."""
374373

375374
pass

src/snowflake/connector/network.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@
187187
OAUTH_AUTHENTICATOR = "OAUTH"
188188
ID_TOKEN_AUTHENTICATOR = "ID_TOKEN"
189189
USR_PWD_MFA_AUTHENTICATOR = "USERNAME_PASSWORD_MFA"
190+
PROGRAMMATIC_ACCESS_TOKEN = "PROGRAMMATIC_ACCESS_TOKEN"
190191

191192

192193
def is_retryable_http_code(code: int) -> bool:

0 commit comments

Comments
 (0)