Skip to content

Commit

Permalink
feat(trino): add support for user impersonation (apache#14843)
Browse files Browse the repository at this point in the history
* trino impersonation feature

* Extra options label update

* Update superset/db_engine_specs/trino.py

Co-authored-by: Đặng Minh Dũng <dungdm93@live.com>

Co-authored-by: rijojoseph01 <rijo.joseph@myntra.com>
Co-authored-by: Đặng Minh Dũng <dungdm93@live.com>
  • Loading branch information
3 people authored and cccs-RyanS committed Dec 17, 2021
1 parent 94e8abc commit 4195c29
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 4 deletions.
Expand Up @@ -292,12 +292,12 @@ const ExtraOptions = ({
checked={!!db?.impersonate_user}
onChange={onInputChange}
labelText={t(
'Impersonate Logged In User (Presto, Hive, and GSheets)',
'Impersonate Logged In User (Presto, Trino, Hive, and GSheets)',
)}
/>
<InfoTooltip
tooltip={t(
'If Presto, all the queries in SQL Lab are going to be executed as the ' +
'If Presto or Trino, all the queries in SQL Lab are going to be executed as the ' +
'currently logged on user who must have permission to run them. If Hive ' +
'and hive.server2.enable.doAs is enabled, will run the queries as ' +
'service account, but impersonate the currently logged on user via ' +
Expand Down
38 changes: 36 additions & 2 deletions superset/db_engine_specs/trino.py
Expand Up @@ -15,10 +15,10 @@
# specific language governing permissions and limitations
# under the License.
from datetime import datetime
from typing import Optional
from typing import Any, Dict, Optional
from urllib import parse

from sqlalchemy.engine.url import URL
from sqlalchemy.engine.url import make_url, URL

from superset.db_engine_specs.base import BaseEngineSpec
from superset.utils import core as utils
Expand Down Expand Up @@ -69,3 +69,37 @@ def adjust_database_uri(
selected_schema = parse.quote(selected_schema, safe="")
database = database.split("/")[0] + "/" + selected_schema
uri.database = database

@classmethod
def update_impersonation_config(
cls, connect_args: Dict[str, Any], uri: str, username: Optional[str],
) -> None:
"""
Update a configuration dictionary
that can set the correct properties for impersonating users
:param connect_args: config to be updated
:param uri: URI string
:param impersonate_user: Flag indicating if impersonation is enabled
:param username: Effective username
:return: None
"""
url = make_url(uri)
backend_name = url.get_backend_name()

# Must be Trino connection, enable impersonation, and set optional param
# auth=LDAP|KERBEROS
# Set principal_username=$effective_username
if backend_name == "trino" and username is not None:
connect_args["user"] = username

@classmethod
def modify_url_for_impersonation(
cls, url: URL, impersonate_user: bool, username: Optional[str]
) -> None:
"""
Modify the SQL Alchemy URL object with the user to impersonate if applicable.
:param url: SQLAlchemy URL object
:param impersonate_user: Flag indicating if impersonation is enabled
:param username: Effective username
"""
# Do nothing and let update_impersonation_config take care of impersonation
27 changes: 27 additions & 0 deletions tests/model_tests.py
Expand Up @@ -157,6 +157,33 @@ def test_impersonate_user_presto(self, mocked_create_engine):
"password": "original_user_password",
}

@mock.patch("superset.models.core.create_engine")
def test_impersonate_user_trino(self, mocked_create_engine):
uri = "trino://localhost"
principal_user = "logged_in_user"

model = Database(database_name="test_database", sqlalchemy_uri=uri)

model.impersonate_user = True
model.get_sqla_engine(user_name=principal_user)
call_args = mocked_create_engine.call_args

assert str(call_args[0][0]) == "trino://localhost"

assert call_args[1]["connect_args"] == {
"user": "logged_in_user",
}

uri = "trino://original_user:original_user_password@localhost"
model = Database(database_name="test_database", sqlalchemy_uri=uri)
model.impersonate_user = True
model.get_sqla_engine(user_name=principal_user)
call_args = mocked_create_engine.call_args

assert str(call_args[0][0]) == "trino://original_user@localhost"

assert call_args[1]["connect_args"] == {"user": "logged_in_user"}

@mock.patch("superset.models.core.create_engine")
def test_impersonate_user_hive(self, mocked_create_engine):
uri = "hive://localhost"
Expand Down

0 comments on commit 4195c29

Please sign in to comment.