Skip to content

Commit a33b593

Browse files
committed
Add allow_insecure_auth connection arg
1 parent 1e95bbf commit a33b593

File tree

6 files changed

+91
-5
lines changed

6 files changed

+91
-5
lines changed

README.md

+35
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,41 @@ It follows the interface for `KerberosAuthentication`, but is using
365365
)
366366
```
367367

368+
369+
### Allow Insecure Authentication
370+
371+
Authentication attempts are automatically enforced to use HTTPS. Although not recommended, you can enable authentication over HTTP by adding `allow_insecure_auth` connection argument.
372+
373+
- DBAPI
374+
375+
```python
376+
from trino.dbapi import connect
377+
...
378+
379+
conn = connect(
380+
auth=...,
381+
allow_insecure_auth=True,
382+
...
383+
)
384+
```
385+
386+
- SQLAlchemy
387+
388+
```python
389+
from sqlalchemy import create_engine
390+
391+
engine = create_engine("trino://<username>:<password>@<host>:<port>/<catalog>?allow_insecure_auth=true")
392+
393+
# or as connect_args
394+
engine = create_engine(
395+
"trino://<username>@<host>:<port>/<catalog>",
396+
connect_args={
397+
"auth"=...,
398+
"allow_insecure_auth": True,
399+
}
400+
)
401+
```
402+
368403
## User impersonation
369404

370405
In the case where user who submits the query is not the same as user who authenticates to Trino server (e.g in Superset),

tests/unit/sqlalchemy/test_dialect.py

+13
Original file line numberDiff line numberDiff line change
@@ -324,3 +324,16 @@ def test_trino_connection_oauth2_auth():
324324

325325
assert cparams['http_scheme'] == "https"
326326
assert isinstance(cparams['auth'], OAuth2Authentication)
327+
328+
329+
def test_trino_connection_allow_insecure_auth():
330+
dialect = TrinoDialect()
331+
username = 'trino-user'
332+
password = 'trino-bunny'
333+
url = make_url(f'trino://{username}:{password}@host?allow_insecure_auth=true')
334+
_, cparams = dialect.create_connect_args(url)
335+
336+
assert 'http_scheme' not in cparams
337+
assert isinstance(cparams['auth'], BasicAuthentication)
338+
assert cparams['auth']._username == username
339+
assert cparams['auth']._password == password

tests/unit/test_client.py

+24
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,30 @@ def test_http_scheme_with_port(mock_get_and_post):
339339
assert parsed_url.port == constants.DEFAULT_TLS_PORT
340340

341341

342+
def test_allow_insecure_auth(mock_get_and_post):
343+
_, post = mock_get_and_post
344+
345+
346+
req = TrinoRequest(
347+
host="coordinator",
348+
port=8080,
349+
client_session=ClientSession(
350+
user="test",
351+
),
352+
http_scheme=constants.HTTP,
353+
354+
auth=KerberosAuthentication(),
355+
allow_insecure_auth=True,
356+
)
357+
358+
req.post("SELECT 1")
359+
post_args, _ = post.call_args
360+
parsed_url = urlparse(post_args[0])
361+
362+
assert parsed_url.scheme == constants.HTTP
363+
assert parsed_url.port == 8080
364+
365+
342366
def test_request_timeout():
343367
timeout = 0.1
344368
http_scheme = "http"

trino/client.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,7 @@ class TrinoRequest:
415415
:param http_scheme: "http" or "https"
416416
:param auth: class that manages user authentication. ``None`` means no
417417
authentication.
418+
:param allow_insecure_auth: allow insecure authentication over HTTP
418419
:max_attempts: maximum number of attempts when sending HTTP requests. An
419420
attempt is an HTTP request. 5 attempts means 4 retries.
420421
:request_timeout: How long (in seconds) to wait for the server to send
@@ -462,6 +463,7 @@ def __init__(
462463
http_session: Optional[Session] = None,
463464
http_scheme: Optional[str] = None,
464465
auth: Optional[Authentication] = constants.DEFAULT_AUTH,
466+
allow_insecure_auth: bool = False,
465467
max_attempts: int = MAX_ATTEMPTS,
466468
request_timeout: Union[float, Tuple[float, float]] = constants.DEFAULT_REQUEST_TIMEOUT,
467469
handle_retry=_RetryWithExponentialBackoff(),
@@ -488,8 +490,9 @@ def __init__(
488490
self._http_session.headers.update(self.http_headers)
489491
self._exceptions = self.HTTP_EXCEPTIONS
490492
self._auth = auth
493+
self._allow_insecure_auth = allow_insecure_auth
491494
if self._auth:
492-
if self._http_scheme == constants.HTTP:
495+
if self._http_scheme == constants.HTTP and not self._allow_insecure_auth:
493496
raise ValueError("cannot use authentication with HTTP")
494497
self._auth.set_http_session(self._http_session)
495498
self._exceptions += self._auth.get_exceptions()

trino/dbapi.py

+3
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ def __init__(
152152
http_headers=None,
153153
http_scheme=constants.HTTP,
154154
auth=constants.DEFAULT_AUTH,
155+
allow_insecure_auth=False,
155156
extra_credential=None,
156157
max_attempts=constants.DEFAULT_MAX_ATTEMPTS,
157158
request_timeout=constants.DEFAULT_REQUEST_TIMEOUT,
@@ -205,6 +206,7 @@ def __init__(
205206
self.http_headers = http_headers
206207
self.http_scheme = http_scheme if not parsed_host.scheme else parsed_host.scheme
207208
self.auth = auth
209+
self.allow_insecure_auth = allow_insecure_auth
208210
self.extra_credential = extra_credential
209211
self.max_attempts = max_attempts
210212
self.request_timeout = request_timeout
@@ -264,6 +266,7 @@ def _create_request(self):
264266
self._http_session,
265267
self.http_scheme,
266268
self.auth,
269+
self.allow_insecure_auth,
267270
self.max_attempts,
268271
self.request_timeout,
269272
)

trino/sqlalchemy/dialect.py

+12-4
Original file line numberDiff line numberDiff line change
@@ -130,22 +130,30 @@ def create_connect_args(self, url: URL) -> Tuple[Sequence[Any], Mapping[str, Any
130130
if url.username:
131131
kwargs["user"] = unquote_plus(url.username)
132132

133+
allow_insecure_auth = "allow_insecure_auth" in url.query
134+
if allow_insecure_auth:
135+
kwargs["allow_insecure_auth"] = True
136+
133137
if url.password:
134138
if not url.username:
135139
raise ValueError("Username is required when specify password in connection URL")
136-
kwargs["http_scheme"] = "https"
140+
if not allow_insecure_auth:
141+
kwargs["http_scheme"] = "https"
137142
kwargs["auth"] = BasicAuthentication(unquote_plus(url.username), unquote_plus(url.password))
138143

139144
if "access_token" in url.query:
140-
kwargs["http_scheme"] = "https"
145+
if not allow_insecure_auth:
146+
kwargs["http_scheme"] = "https"
141147
kwargs["auth"] = JWTAuthentication(unquote_plus(url.query["access_token"]))
142148

143149
if "cert" in url.query and "key" in url.query:
144-
kwargs["http_scheme"] = "https"
150+
if not allow_insecure_auth:
151+
kwargs["http_scheme"] = "https"
145152
kwargs["auth"] = CertificateAuthentication(unquote_plus(url.query['cert']), unquote_plus(url.query['key']))
146153

147154
if "externalAuthentication" in url.query:
148-
kwargs["http_scheme"] = "https"
155+
if not allow_insecure_auth:
156+
kwargs["http_scheme"] = "https"
149157
kwargs["auth"] = OAuth2Authentication()
150158

151159
if "source" in url.query:

0 commit comments

Comments
 (0)