# Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. import inspect from superset import app, appbuilder, security_manager from .base_tests import SupersetTestCase def get_perm_tuples(role_name): perm_set = set() for perm in security_manager.find_role(role_name).permissions: perm_set.add((perm.permission.name, perm.view_menu.name)) return perm_set class RolePermissionTests(SupersetTestCase): """Testing export import functionality for dashboards""" def assert_can_read(self, view_menu, permissions_set): self.assertIn(('can_show', view_menu), permissions_set) self.assertIn(('can_list', view_menu), permissions_set) def assert_can_write(self, view_menu, permissions_set): self.assertIn(('can_add', view_menu), permissions_set) self.assertIn(('can_download', view_menu), permissions_set) self.assertIn(('can_delete', view_menu), permissions_set) self.assertIn(('can_edit', view_menu), permissions_set) def assert_cannot_write(self, view_menu, permissions_set): self.assertNotIn(('can_add', view_menu), permissions_set) self.assertNotIn(('can_download', view_menu), permissions_set) self.assertNotIn(('can_delete', view_menu), permissions_set) self.assertNotIn(('can_edit', view_menu), permissions_set) self.assertNotIn(('can_save', view_menu), permissions_set) def assert_can_all(self, view_menu, permissions_set): self.assert_can_read(view_menu, permissions_set) self.assert_can_write(view_menu, permissions_set) def assert_cannot_gamma(self, perm_set): self.assert_cannot_write('DruidColumnInlineView', perm_set) def assert_can_gamma(self, perm_set): self.assert_can_read('DatabaseAsync', perm_set) self.assert_can_read('TableModelView', perm_set) # make sure that user can create slices and dashboards self.assert_can_all('SliceModelView', perm_set) self.assert_can_all('DashboardModelView', perm_set) self.assertIn(('can_add_slices', 'Superset'), perm_set) self.assertIn(('can_copy_dash', 'Superset'), perm_set) self.assertIn(('can_created_dashboards', 'Superset'), perm_set) self.assertIn(('can_created_slices', 'Superset'), perm_set) self.assertIn(('can_csv', 'Superset'), perm_set) self.assertIn(('can_dashboard', 'Superset'), perm_set) self.assertIn(('can_explore', 'Superset'), perm_set) self.assertIn(('can_explore_json', 'Superset'), perm_set) self.assertIn(('can_fave_dashboards', 'Superset'), perm_set) self.assertIn(('can_fave_slices', 'Superset'), perm_set) self.assertIn(('can_save_dash', 'Superset'), perm_set) self.assertIn(('can_slice', 'Superset'), perm_set) self.assertIn(('can_explore', 'Superset'), perm_set) self.assertIn(('can_explore_json', 'Superset'), perm_set) def assert_can_alpha(self, perm_set): self.assert_can_all('SqlMetricInlineView', perm_set) self.assert_can_all('TableColumnInlineView', perm_set) self.assert_can_all('TableModelView', perm_set) self.assert_can_all('DruidColumnInlineView', perm_set) self.assert_can_all('DruidDatasourceModelView', perm_set) self.assert_can_all('DruidMetricInlineView', perm_set) self.assertIn( ('all_datasource_access', 'all_datasource_access'), perm_set) self.assertIn(('muldelete', 'DruidDatasourceModelView'), perm_set) def assert_cannot_alpha(self, perm_set): if app.config.get('ENABLE_ACCESS_REQUEST'): self.assert_cannot_write('AccessRequestsModelView', perm_set) self.assert_can_all('AccessRequestsModelView', perm_set) self.assert_cannot_write('Queries', perm_set) self.assert_cannot_write('RoleModelView', perm_set) self.assert_cannot_write('UserDBModelView', perm_set) def assert_can_admin(self, perm_set): self.assert_can_all('DatabaseAsync', perm_set) self.assert_can_all('DatabaseView', perm_set) self.assert_can_all('DruidClusterModelView', perm_set) self.assert_can_all('RoleModelView', perm_set) self.assert_can_all('UserDBModelView', perm_set) self.assertIn(('all_database_access', 'all_database_access'), perm_set) self.assertIn(('can_override_role_permissions', 'Superset'), perm_set) self.assertIn(('can_sync_druid_source', 'Superset'), perm_set) self.assertIn(('can_override_role_permissions', 'Superset'), perm_set) self.assertIn(('can_approve', 'Superset'), perm_set) def test_is_admin_only(self): self.assertFalse(security_manager.is_admin_only( security_manager.find_permission_view_menu('can_show', 'TableModelView'))) self.assertFalse(security_manager.is_admin_only( security_manager.find_permission_view_menu( 'all_datasource_access', 'all_datasource_access'))) self.assertTrue(security_manager.is_admin_only( security_manager.find_permission_view_menu('can_delete', 'DatabaseView'))) if app.config.get('ENABLE_ACCESS_REQUEST'): self.assertTrue(security_manager.is_admin_only( security_manager.find_permission_view_menu( 'can_show', 'AccessRequestsModelView'))) self.assertTrue(security_manager.is_admin_only( security_manager.find_permission_view_menu( 'can_edit', 'UserDBModelView'))) self.assertTrue(security_manager.is_admin_only( security_manager.find_permission_view_menu( 'can_approve', 'Superset'))) self.assertTrue(security_manager.is_admin_only( security_manager.find_permission_view_menu( 'all_database_access', 'all_database_access'))) def test_is_alpha_only(self): self.assertFalse(security_manager.is_alpha_only( security_manager.find_permission_view_menu('can_show', 'TableModelView'))) self.assertTrue(security_manager.is_alpha_only( security_manager.find_permission_view_menu('muldelete', 'TableModelView'))) self.assertTrue(security_manager.is_alpha_only( security_manager.find_permission_view_menu( 'all_datasource_access', 'all_datasource_access'))) self.assertTrue(security_manager.is_alpha_only( security_manager.find_permission_view_menu( 'can_edit', 'SqlMetricInlineView'))) self.assertTrue(security_manager.is_alpha_only( security_manager.find_permission_view_menu( 'can_delete', 'DruidMetricInlineView'))) def test_is_gamma_pvm(self): self.assertTrue(security_manager.is_gamma_pvm( security_manager.find_permission_view_menu('can_show', 'TableModelView'))) def test_gamma_permissions_basic(self): self.assert_can_gamma(get_perm_tuples('Gamma')) self.assert_cannot_gamma(get_perm_tuples('Gamma')) self.assert_cannot_alpha(get_perm_tuples('Alpha')) def test_alpha_permissions(self): self.assert_can_gamma(get_perm_tuples('Alpha')) self.assert_can_alpha(get_perm_tuples('Alpha')) self.assert_cannot_alpha(get_perm_tuples('Alpha')) def test_admin_permissions(self): self.assert_can_gamma(get_perm_tuples('Admin')) self.assert_can_alpha(get_perm_tuples('Admin')) self.assert_can_admin(get_perm_tuples('Admin')) def test_sql_lab_permissions(self): sql_lab_set = get_perm_tuples('sql_lab') self.assertIn(('can_sql_json', 'Superset'), sql_lab_set) self.assertIn(('can_csv', 'Superset'), sql_lab_set) self.assertIn(('can_search_queries', 'Superset'), sql_lab_set) self.assert_cannot_gamma(sql_lab_set) self.assert_cannot_alpha(sql_lab_set) def test_granter_permissions(self): granter_set = get_perm_tuples('granter') self.assertIn(('can_override_role_permissions', 'Superset'), granter_set) self.assertIn(('can_approve', 'Superset'), granter_set) self.assert_cannot_gamma(granter_set) self.assert_cannot_alpha(granter_set) def test_gamma_permissions(self): def assert_can_read(view_menu): self.assertIn(('can_show', view_menu), gamma_perm_set) self.assertIn(('can_list', view_menu), gamma_perm_set) def assert_can_write(view_menu): self.assertIn(('can_add', view_menu), gamma_perm_set) self.assertIn(('can_download', view_menu), gamma_perm_set) self.assertIn(('can_delete', view_menu), gamma_perm_set) self.assertIn(('can_edit', view_menu), gamma_perm_set) def assert_cannot_write(view_menu): self.assertNotIn(('can_add', view_menu), gamma_perm_set) self.assertNotIn(('can_download', view_menu), gamma_perm_set) self.assertNotIn(('can_delete', view_menu), gamma_perm_set) self.assertNotIn(('can_edit', view_menu), gamma_perm_set) self.assertNotIn(('can_save', view_menu), gamma_perm_set) def assert_can_all(view_menu): assert_can_read(view_menu) assert_can_write(view_menu) gamma_perm_set = set() for perm in security_manager.find_role('Gamma').permissions: gamma_perm_set.add((perm.permission.name, perm.view_menu.name)) # check read only perms assert_can_read('TableModelView') assert_cannot_write('DruidColumnInlineView') # make sure that user can create slices and dashboards assert_can_all('SliceModelView') assert_can_all('DashboardModelView') self.assertIn(('can_add_slices', 'Superset'), gamma_perm_set) self.assertIn(('can_copy_dash', 'Superset'), gamma_perm_set) self.assertIn(('can_created_dashboards', 'Superset'), gamma_perm_set) self.assertIn(('can_created_slices', 'Superset'), gamma_perm_set) self.assertIn(('can_csv', 'Superset'), gamma_perm_set) self.assertIn(('can_dashboard', 'Superset'), gamma_perm_set) self.assertIn(('can_explore', 'Superset'), gamma_perm_set) self.assertIn(('can_explore_json', 'Superset'), gamma_perm_set) self.assertIn(('can_fave_dashboards', 'Superset'), gamma_perm_set) self.assertIn(('can_fave_slices', 'Superset'), gamma_perm_set) self.assertIn(('can_save_dash', 'Superset'), gamma_perm_set) self.assertIn(('can_slice', 'Superset'), gamma_perm_set) def test_views_are_secured(self): """Preventing the addition of unsecured views without has_access decorator""" # These FAB views are secured in their body as opposed to by decorators method_whitelist = ('action', 'action_post') # List of redirect & other benign views views_whitelist = [ ['MyIndexView', 'index'], ['UtilView', 'back'], ['LocaleView', 'index'], ['AuthDBView', 'login'], ['AuthDBView', 'logout'], ['R', 'index'], ['Superset', 'log'], ['Superset', 'theme'], ['Superset', 'welcome'], ] unsecured_views = [] for view_class in appbuilder.baseviews: class_name = view_class.__class__.__name__ for name, value in inspect.getmembers(view_class, predicate=inspect.ismethod): if ( name not in method_whitelist and [class_name, name] not in views_whitelist and hasattr(value, '_urls') and not hasattr(value, '_permission_name') ): unsecured_views.append((class_name, name)) if unsecured_views: view_str = '\n'.join([str(v) for v in unsecured_views]) raise Exception(f'Some views are not secured:\n{view_str}')