diff --git a/superset/utils.py b/superset/utils.py index cf104fabe26fd..d85bf4b195e0e 100644 --- a/superset/utils.py +++ b/superset/utils.py @@ -25,7 +25,13 @@ from email.mime.multipart import MIMEMultipart from email.mime.application import MIMEApplication from email.utils import formatdate -from flask import flash, Markup, render_template +from flask import flash, Markup, render_template, url_for, redirect, request +from flask_appbuilder.const import ( + LOGMSG_ERR_SEC_ACCESS_DENIED, + FLAMSG_ERR_SEC_ACCESS_DENIED, + PERMISSION_PREFIX +) +from flask_appbuilder._compat import as_unicode from flask_babel import gettext as __ from past.builtins import basestring from pydruid.utils.having import Having @@ -513,3 +519,35 @@ def get_email_address_list(address_string): else: address_string = [address_string] return address_string + + +# Forked from the flask_appbuilder.security.decorators +# TODO(bkyryliuk): contribute it back to FAB +def has_access(f): + """ + Use this decorator to enable granular security permissions to your + methods. Permissions will be associated to a role, and roles are + associated to users. + + By default the permission's name is the methods name. + """ + if hasattr(f, '_permission_name'): + permission_str = f._permission_name + else: + permission_str = f.__name__ + + def wraps(self, *args, **kwargs): + permission_str = PERMISSION_PREFIX + f._permission_name + if self.appbuilder.sm.has_access( + permission_str, self.__class__.__name__): + return f(self, *args, **kwargs) + else: + logging.warning(LOGMSG_ERR_SEC_ACCESS_DENIED.format( + permission_str, self.__class__.__name__)) + flash(as_unicode(FLAMSG_ERR_SEC_ACCESS_DENIED), "danger") + # adds next arg to forward to the original path once user is logged in. + return redirect(url_for( + self.appbuilder.sm.auth_view.__class__.__name__ + ".login", + next=request.path)) + f._permission_name = permission_str + return functools.update_wrapper(wraps, f) diff --git a/superset/views.py b/superset/views.py index 4e5cd7be53935..32f59044bba8d 100755 --- a/superset/views.py +++ b/superset/views.py @@ -21,7 +21,7 @@ from flask_appbuilder import ModelView, CompactCRUDMixin, BaseView, expose from flask_appbuilder.actions import action from flask_appbuilder.models.sqla.interface import SQLAInterface -from flask_appbuilder.security.decorators import has_access, has_access_api +from flask_appbuilder.security.decorators import has_access_api from flask_appbuilder.widgets import ListWidget from flask_appbuilder.models.sqla.filters import BaseFilter from flask_appbuilder.security.sqla import models as ab_models @@ -38,6 +38,7 @@ app, appbuilder, cache, db, models, sm, sql_lab, sql_parse, results_backend, security, viz, utils, ) +from superset.utils import has_access from superset.source_registry import SourceRegistry from superset.models import DatasourceAccessRequest as DAR from superset.sql_parse import SupersetQuery