mirror of
https://github.com/apache/superset.git
synced 2026-04-19 08:04:53 +00:00
Related Msg: sqlalchemy.orm.exc.ObjectDeletedError: Instance '<PermissionView at 0x7f10306b0e90>' has been deleted, or its row is otherwise not present.
219 lines
6.4 KiB
Python
219 lines
6.4 KiB
Python
"""A set of constants and methods to manage permissions and security"""
|
|
from __future__ import absolute_import
|
|
from __future__ import division
|
|
from __future__ import print_function
|
|
from __future__ import unicode_literals
|
|
|
|
import logging
|
|
from flask_appbuilder.security.sqla import models as ab_models
|
|
|
|
from superset import conf, db, sm
|
|
from superset.models import core as models
|
|
from superset.connectors.connector_registry import ConnectorRegistry
|
|
|
|
|
|
READ_ONLY_MODEL_VIEWS = {
|
|
'DatabaseAsync',
|
|
'DatabaseView',
|
|
'DruidClusterModelView',
|
|
}
|
|
|
|
GAMMA_READ_ONLY_MODEL_VIEWS = {
|
|
'SqlMetricInlineView',
|
|
'TableColumnInlineView',
|
|
'TableModelView',
|
|
'DruidColumnInlineView',
|
|
'DruidDatasourceModelView',
|
|
'DruidMetricInlineView',
|
|
} | READ_ONLY_MODEL_VIEWS
|
|
|
|
ADMIN_ONLY_VIEW_MENUS = {
|
|
'AccessRequestsModelView',
|
|
'Manage',
|
|
'SQL Lab',
|
|
'Queries',
|
|
'Refresh Druid Metadata',
|
|
'ResetPasswordView',
|
|
'RoleModelView',
|
|
'Security',
|
|
'UserDBModelView',
|
|
}
|
|
|
|
ADMIN_ONLY_PERMISSIONS = {
|
|
'all_database_access',
|
|
'can_sql_json', # TODO: move can_sql_json to sql_lab role
|
|
'can_override_role_permissions',
|
|
'can_sync_druid_source',
|
|
'can_override_role_permissions',
|
|
'can_approve',
|
|
'can_update_role',
|
|
}
|
|
|
|
READ_ONLY_PERMISSION = {
|
|
'can_show',
|
|
'can_list',
|
|
}
|
|
|
|
ALPHA_ONLY_PERMISSIONS = set([
|
|
'muldelete',
|
|
'all_datasource_access',
|
|
])
|
|
|
|
OBJECT_SPEC_PERMISSIONS = set([
|
|
'database_access',
|
|
'schema_access',
|
|
'datasource_access',
|
|
'metric_access',
|
|
])
|
|
|
|
|
|
def merge_perm(sm, permission_name, view_menu_name):
|
|
# Implementation copied from sm.find_permission_view_menu.
|
|
# TODO: use sm.find_permission_view_menu once issue
|
|
# https://github.com/airbnb/superset/issues/1944 is resolved.
|
|
permission = sm.find_permission(permission_name)
|
|
view_menu = sm.find_view_menu(view_menu_name)
|
|
pv = None
|
|
if permission and view_menu:
|
|
pv = sm.get_session.query(sm.permissionview_model).filter_by(
|
|
permission=permission, view_menu=view_menu).first()
|
|
if not pv and permission_name and view_menu_name:
|
|
sm.add_permission_view_menu(permission_name, view_menu_name)
|
|
|
|
|
|
def is_user_defined_permission(perm):
|
|
return perm.permission.name in OBJECT_SPEC_PERMISSIONS
|
|
|
|
|
|
def get_or_create_main_db():
|
|
logging.info("Creating database reference")
|
|
dbobj = (
|
|
db.session.query(models.Database)
|
|
.filter_by(database_name='main')
|
|
.first()
|
|
)
|
|
if not dbobj:
|
|
dbobj = models.Database(database_name="main")
|
|
dbobj.set_sqlalchemy_uri(conf.get("SQLALCHEMY_DATABASE_URI"))
|
|
dbobj.expose_in_sqllab = True
|
|
dbobj.allow_run_sync = True
|
|
db.session.add(dbobj)
|
|
db.session.commit()
|
|
return dbobj
|
|
|
|
|
|
def is_admin_only(pvm):
|
|
# not readonly operations on read only model views allowed only for admins
|
|
if (pvm.view_menu.name in READ_ONLY_MODEL_VIEWS and
|
|
pvm.permission.name not in READ_ONLY_PERMISSION):
|
|
return True
|
|
return (pvm.view_menu.name in ADMIN_ONLY_VIEW_MENUS or
|
|
pvm.permission.name in ADMIN_ONLY_PERMISSIONS)
|
|
|
|
|
|
def is_alpha_only(pvm):
|
|
if (pvm.view_menu.name in GAMMA_READ_ONLY_MODEL_VIEWS and
|
|
pvm.permission.name not in READ_ONLY_PERMISSION):
|
|
return True
|
|
return pvm.permission.name in ALPHA_ONLY_PERMISSIONS
|
|
|
|
|
|
def is_admin_pvm(pvm):
|
|
return not is_user_defined_permission(pvm)
|
|
|
|
|
|
def is_alpha_pvm(pvm):
|
|
return not (is_user_defined_permission(pvm) or is_admin_only(pvm))
|
|
|
|
|
|
def is_gamma_pvm(pvm):
|
|
return not (is_user_defined_permission(pvm) or is_admin_only(pvm) or
|
|
is_alpha_only(pvm))
|
|
|
|
|
|
def is_sql_lab_pvm(pvm):
|
|
return pvm.view_menu.name in {'SQL Lab'} or pvm.permission.name in {
|
|
'can_sql_json', 'can_csv', 'can_search_queries'}
|
|
|
|
|
|
def is_granter_pvm(pvm):
|
|
return pvm.permission.name in {'can_override_role_permissions',
|
|
'can_approve'}
|
|
|
|
|
|
def set_role(role_name, pvm_check):
|
|
logging.info("Syncing {} perms".format(role_name))
|
|
sesh = sm.get_session()
|
|
pvms = sesh.query(ab_models.PermissionView).all()
|
|
pvms = [p for p in pvms if p.permission and p.view_menu]
|
|
role = sm.add_role(role_name)
|
|
role_pvms = [p for p in pvms if pvm_check(p)]
|
|
role.permissions = role_pvms
|
|
sesh.merge(role)
|
|
sesh.commit()
|
|
|
|
|
|
def create_custom_permissions():
|
|
# Global perms
|
|
merge_perm(sm, 'all_datasource_access', 'all_datasource_access')
|
|
merge_perm(sm, 'all_database_access', 'all_database_access')
|
|
|
|
|
|
def create_missing_perms():
|
|
"""Creates missing perms for datasources, schemas and metrics"""
|
|
|
|
logging.info(
|
|
"Fetching a set of all perms to lookup which ones are missing")
|
|
all_pvs = set()
|
|
for pv in sm.get_session.query(sm.permissionview_model).all():
|
|
if pv.permission and pv.view_menu:
|
|
all_pvs.add((pv.permission.name, pv.view_menu.name))
|
|
|
|
def merge_pv(view_menu, perm):
|
|
"""Create permission view menu only if it doesn't exist"""
|
|
if view_menu and perm and (view_menu, perm) not in all_pvs:
|
|
merge_perm(sm, view_menu, perm)
|
|
|
|
logging.info("Creating missing datasource permissions.")
|
|
datasources = ConnectorRegistry.get_all_datasources(db.session)
|
|
for datasource in datasources:
|
|
merge_pv('datasource_access', datasource.get_perm())
|
|
merge_pv('schema_access', datasource.schema_perm)
|
|
|
|
logging.info("Creating missing database permissions.")
|
|
databases = db.session.query(models.Database).all()
|
|
for database in databases:
|
|
merge_pv('database_access', database.perm)
|
|
|
|
logging.info("Creating missing metrics permissions")
|
|
metrics = []
|
|
for datasource_class in ConnectorRegistry.sources.values():
|
|
metrics += list(db.session.query(datasource_class.metric_class).all())
|
|
|
|
for metric in metrics:
|
|
if (metric.is_restricted):
|
|
merge_pv('metric_access', metric.perm)
|
|
|
|
|
|
def sync_role_definitions():
|
|
"""Inits the Superset application with security roles and such"""
|
|
logging.info("Syncing role definition")
|
|
|
|
get_or_create_main_db()
|
|
create_custom_permissions()
|
|
|
|
# Creating default roles
|
|
set_role('Admin', is_admin_pvm)
|
|
set_role('Alpha', is_alpha_pvm)
|
|
set_role('Gamma', is_gamma_pvm)
|
|
set_role('granter', is_granter_pvm)
|
|
set_role('sql_lab', is_sql_lab_pvm)
|
|
|
|
if conf.get('PUBLIC_ROLE_LIKE_GAMMA', False):
|
|
set_role('Public', is_gamma_pvm)
|
|
|
|
create_missing_perms()
|
|
|
|
# commit role and view menu updates
|
|
sm.get_session.commit()
|