You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by be...@apache.org on 2018/12/07 20:19:25 UTC
[incubator-superset] branch master updated: Hook for auditing
queries (#6484)
This is an automated email from the ASF dual-hosted git repository.
beto pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-superset.git
The following commit(s) were added to refs/heads/master by this push:
new 5168c69 Hook for auditing queries (#6484)
5168c69 is described below
commit 5168c69a25bcbcb3b06b950286b63fa373113675
Author: Beto Dealmeida <ro...@dealmeida.net>
AuthorDate: Fri Dec 7 12:19:19 2018 -0800
Hook for auditing queries (#6484)
* Hook for auditing queries
* Address comments
* Use __name__
---
superset/config.py | 6 ++++++
superset/models/core.py | 8 ++++++++
superset/sql_lab.py | 9 +++++++++
3 files changed, 23 insertions(+)
diff --git a/superset/config.py b/superset/config.py
index 73e5a2c..1613e75 100644
--- a/superset/config.py
+++ b/superset/config.py
@@ -281,6 +281,12 @@ ROLLOVER = 'midnight'
INTERVAL = 1
BACKUP_COUNT = 30
+# Custom logger for auditing queries. This can be used to send ran queries to a
+# structured immutable store for auditing purposes. The function is called for
+# every query ran, in both SQL Lab and charts/dashboards.
+# def QUERY_LOGGER(database, query, schema=None, user=None, client=None):
+# pass
+
# Set this API key to enable Mapbox visualizations
MAPBOX_API_KEY = os.environ.get('MAPBOX_API_KEY', '')
diff --git a/superset/models/core.py b/superset/models/core.py
index 1323eed..6f520aa 100644
--- a/superset/models/core.py
+++ b/superset/models/core.py
@@ -43,6 +43,7 @@ from urllib import parse # noqa
config = app.config
custom_password_store = config.get('SQLALCHEMY_CUSTOM_PASSWORD_STORE')
stats_logger = config.get('STATS_LOGGER')
+log_query = config.get('QUERY_LOGGER')
metadata = Model.metadata # pylint: disable=no-member
PASSWORD_MASK = 'X' * 10
@@ -801,6 +802,7 @@ class Database(Model, AuditMixinNullable, ImportMixin):
def get_df(self, sql, schema):
sqls = [str(s).strip().strip(';') for s in sqlparse.parse(sql)]
engine = self.get_sqla_engine(schema=schema)
+ username = utils.get_username()
def needs_conversion(df_series):
if df_series.empty:
@@ -809,12 +811,18 @@ class Database(Model, AuditMixinNullable, ImportMixin):
return True
return False
+ def _log_query(sql):
+ if log_query:
+ log_query(engine.url, sql, schema, username, __name__)
+
with closing(engine.raw_connection()) as conn:
with closing(conn.cursor()) as cursor:
for sql in sqls[:-1]:
+ _log_query(sql)
self.db_engine_spec.execute(cursor, sql)
cursor.fetchall()
+ _log_query(sqls[-1])
self.db_engine_spec.execute(cursor, sqls[-1])
if cursor.description is not None:
diff --git a/superset/sql_lab.py b/superset/sql_lab.py
index 1d4171b..0969147 100644
--- a/superset/sql_lab.py
+++ b/superset/sql_lab.py
@@ -26,6 +26,7 @@ config = app.config
celery_app = get_celery_app(config)
stats_logger = app.config.get('STATS_LOGGER')
SQLLAB_TIMEOUT = config.get('SQLLAB_ASYNC_TIME_LIMIT_SEC', 600)
+log_query = config.get('QUERY_LOGGER')
class SqlLabException(Exception):
@@ -180,6 +181,14 @@ def execute_sql(
logging.info('Running query: \n{}'.format(executed_sql))
logging.info(query.executed_sql)
query_start_time = now_as_float()
+ if log_query:
+ log_query(
+ query.database.sqlalchemy_uri,
+ query.executed_sql,
+ query.schema,
+ user_name,
+ __name__,
+ )
db_engine_spec.execute(cursor, query.executed_sql, async_=True)
logging.info('Handling cursor')
db_engine_spec.handle_cursor(cursor, query, session)