From 04748b4cdac7a4fd53f5d7716a36ceebea7bf7ca Mon Sep 17 00:00:00 2001 From: Maxime Beauchemin Date: Tue, 9 May 2017 13:36:49 -0700 Subject: [PATCH] [SQL Lab] fix gamma metadata access (#2702) --- superset/utils.py | 9 +++--- superset/views/base.py | 71 +++++++++++++++++++++++++++++------------- superset/views/core.py | 6 ++-- 3 files changed, 58 insertions(+), 28 deletions(-) diff --git a/superset/utils.py b/superset/utils.py index 2cfd7a59928d8..655ccad20c798 100644 --- a/superset/utils.py +++ b/superset/utils.py @@ -74,11 +74,10 @@ class SupersetTemplateException(SupersetException): def can_access(sm, permission_name, view_name, user): """Protecting from has_access failing from missing perms/view""" - return ( - sm.is_item_public(permission_name, view_name) or - (not user.is_anonymous() and - sm._has_view_access(user, permission_name, view_name)) - ) + if user.is_anonymous(): + return sm.is_item_public(permission_name, view_name) + else: + return sm._has_view_access(user, permission_name, view_name) def flasher(msg, severity=None): diff --git a/superset/views/base.py b/superset/views/base.py index 198b58d42d794..7d57f09027b5b 100644 --- a/superset/views/base.py +++ b/superset/views/base.py @@ -15,6 +15,7 @@ from superset import appbuilder, conf, db, utils, sm, sql_parse from superset.connectors.connector_registry import ConnectorRegistry +from superset.connectors.sqla.models import SqlaTable def get_error_msg(): @@ -101,8 +102,7 @@ def datasource_access_by_name( return True schema_perm = utils.get_schema_perm(database, schema) - if schema and utils.can_access( - sm, 'schema_access', schema_perm, g.user): + if schema and self.can_access('schema_access', schema_perm): return True datasources = ConnectorRegistry.query_datasources_by_name( @@ -130,32 +130,61 @@ def rejected_datasources(self, sql, database, schema): t for t in superset_query.tables if not self.datasource_access_by_fullname(database, t, schema)] + def user_datasource_perms(self): + datasource_perms = set() + for r in g.user.roles: + for perm in r.permissions: + if ( + perm.permission and + 'datasource_access' == perm.permission.name): + datasource_perms.add(perm.view_menu.name) + return datasource_perms + + def schemas_accessible_by_user(self, database, schemas): + if self.database_access(database) or self.all_datasource_access(): + return schemas + + subset = set() + for schema in schemas: + schema_perm = utils.get_schema_perm(database, schema) + if self.can_access('schema_access', schema_perm): + subset.add(schema) + + perms = self.user_datasource_perms() + if perms: + tables = ( + db.session.query(SqlaTable) + .filter( + SqlaTable.perm.in_(perms), + SqlaTable.database_id == database.id, + ) + .all() + ) + for t in tables: + if t.schema: + subset.add(t.schema) + return sorted(list(subset)) + def accessible_by_user(self, database, datasource_names, schema=None): if self.database_access(database) or self.all_datasource_access(): return datasource_names - schema_perm = utils.get_schema_perm(database, schema) - if schema and utils.can_access( - sm, 'schema_access', schema_perm, g.user): - return datasource_names + if schema: + schema_perm = utils.get_schema_perm(database, schema) + if self.can_access('schema_access', schema_perm): + return datasource_names - role_ids = set([role.id for role in g.user.roles]) - # TODO: cache user_perms or user_datasources - PV = ab_models.PermissionView - - pv_role = PV.role # pylint: disable=no-member - user_pvms = ( - db.session.query(PV) - .join(ab_models.Permission) - .filter(ab_models.Permission.name == 'datasource_access') - .filter(pv_role.any(ab_models.Role.id.in_(role_ids))) - .all() - ) - user_perms = set([pvm.view_menu.name for pvm in user_pvms]) + user_perms = self.user_datasource_perms() user_datasources = ConnectorRegistry.query_datasources_by_permissions( db.session, database, user_perms) - full_names = set([d.full_name for d in user_datasources]) - return [d for d in datasource_names if d in full_names] + if schema: + names = { + d.table_name + for d in user_datasources if d.schema == schema} + return [d for d in datasource_names if d in names] + else: + full_names = {d.full_name for d in user_datasources} + return [d for d in datasource_names if d in full_names] class SupersetModelView(ModelView): diff --git a/superset/views/core.py b/superset/views/core.py index c1cee6d2c4ba6..5bfd73017b3f3 100755 --- a/superset/views/core.py +++ b/superset/views/core.py @@ -3,6 +3,7 @@ from __future__ import print_function from __future__ import unicode_literals +from collections import defaultdict from datetime import datetime, timedelta import json import logging @@ -1198,8 +1199,10 @@ def schemas(self, db_id): .filter_by(id=db_id) .one() ) + schemas = database.all_schema_names() + schemas = self.schemas_accessible_by_user(database, schemas) return Response( - json.dumps({'schemas': database.all_schema_names()}), + json.dumps({'schemas': schemas}), mimetype="application/json") @api @@ -2179,7 +2182,6 @@ def profile(self, username): .one() ) roles = {} - from collections import defaultdict permissions = defaultdict(set) for role in user.roles: perms = set()