diff --git a/superset/models.py b/superset/models.py index 649ea782f22c..a8e3fdf6dc0e 100644 --- a/superset/models.py +++ b/superset/models.py @@ -907,6 +907,11 @@ def link(self): return Markup( '{table_name}'.format(**locals())) + @property + def schema_perm(self): + """Returns schema permission if present, database one otherwise.""" + return utils.get_schema_perm(self.database, self.schema) + def get_perm(self): return ( "[{obj.database}].[{obj.table_name}]" @@ -1625,6 +1630,19 @@ def num_cols(self): def name(self): return self.datasource_name + @property + def schema(self): + name_pieces = self.datasource_name.split('.') + if len(name_pieces) > 1: + return name_pieces[0] + else: + return None + + @property + def schema_perm(self): + """Returns schema permission if present, cluster one otherwise.""" + return utils.get_schema_perm(self.cluster, self.schema) + def get_perm(self): return ( "[{obj.cluster_name}].[{obj.datasource_name}]" diff --git a/superset/security.py b/superset/security.py index fbafbe6de2a7..54a618b1ca44 100644 --- a/superset/security.py +++ b/superset/security.py @@ -30,6 +30,7 @@ ADMIN_ONLY_PERMISSIONS = { 'all_database_access', 'datasource_access', + 'schema_access', 'database_access', 'can_sql_json', 'can_override_role_permissions', @@ -50,6 +51,7 @@ 'can_edit', 'can_save', 'datasource_access', + 'schema_access', 'database_access', 'muldelete', 'all_datasource_access', @@ -59,6 +61,7 @@ OBJECT_SPEC_PERMISSIONS = set([ 'database_access', + 'schema_access', 'datasource_access', 'metric_access', ]) @@ -186,6 +189,9 @@ def sync_role_definitions(): for datasource in datasources: perm = datasource.get_perm() sm.add_permission_view_menu('datasource_access', perm) + if datasource.schema: + sm.add_permission_view_menu( + 'schema_access', datasource.schema_perm) if perm != datasource.perm: datasource.perm = perm diff --git a/superset/utils.py b/superset/utils.py index 9e14f0916e12..d4dc2dbe5f3e 100644 --- a/superset/utils.py +++ b/superset/utils.py @@ -330,6 +330,12 @@ def get_datasource_full_name(database_name, datasource_name, schema=None): return "[{}].[{}].[{}]".format(database_name, schema, datasource_name) +def get_schema_perm(database, schema): + if schema: + return "[{}].[{}]".format(database, schema) + return database.perm + + def validate_json(obj): if obj: try: diff --git a/superset/views.py b/superset/views.py index 15438b360c4c..f0458e5f84e3 100755 --- a/superset/views.py +++ b/superset/views.py @@ -61,10 +61,16 @@ def database_access(self, database): self.can_access("database_access", database.perm) ) - def datasource_access(self, datasource): + def schema_access(self, datasource): return ( self.database_access(datasource.database) or self.all_datasource_access() or + self.can_access("schema_access", datasource.schema_perm) + ) + + def datasource_access(self, datasource): + return ( + self.schema_access(datasource) or self.can_access("datasource_access", datasource.perm) ) @@ -575,6 +581,9 @@ class DatabaseView(SupersetModelView, DeleteMixin): # noqa def pre_add(self, db): db.set_sqlalchemy_uri(db.sqlalchemy_uri) security.merge_perm(sm, 'database_access', db.perm) + for schema in db.all_schema_names(): + security.merge_perm( + sm, 'schema_access', utils.get_schema_perm(db, schema)) def pre_update(self, db): self.pre_add(db) @@ -685,6 +694,9 @@ def pre_add(self, table): def post_add(self, table): table.fetch_metadata() security.merge_perm(sm, 'datasource_access', table.perm) + if table.schema: + security.merge_perm(sm, 'schema_access', table.schema_perm) + flash(_( "The table was created. As part of this two phase configuration " "process, you should now click the edit button by " @@ -1049,6 +1061,8 @@ def pre_add(self, datasource): def post_add(self, datasource): datasource.generate_metrics() security.merge_perm(sm, 'datasource_access', datasource.perm) + if datasource.schema: + security.merge_perm(sm, 'schema_access', datasource.schema_perm) def post_update(self, datasource): self.post_add(datasource)