From 40d590fd9dd217f00d852f07a9f63d13deb433d6 Mon Sep 17 00:00:00 2001 From: Bogdan Kyryliuk Date: Mon, 12 Sep 2016 08:57:31 -0700 Subject: [PATCH 1/4] Implement permission request/approve flow --- .../5e4a03ef0bf0_add_request_access_model.py | 36 +++ caravel/models.py | 73 ++++++ caravel/templates/caravel/request_access.html | 24 ++ caravel/views.py | 147 ++++++++++-- tests/base_tests.py | 60 ++++- tests/core_tests.py | 225 +++++++++++++++++- 6 files changed, 545 insertions(+), 20 deletions(-) create mode 100644 caravel/migrations/versions/5e4a03ef0bf0_add_request_access_model.py create mode 100644 caravel/templates/caravel/request_access.html diff --git a/caravel/migrations/versions/5e4a03ef0bf0_add_request_access_model.py b/caravel/migrations/versions/5e4a03ef0bf0_add_request_access_model.py new file mode 100644 index 0000000000000..d505eef0d6d31 --- /dev/null +++ b/caravel/migrations/versions/5e4a03ef0bf0_add_request_access_model.py @@ -0,0 +1,36 @@ +"""Add access_request table to manage requests to access datastores. + +Revision ID: 5e4a03ef0bf0 +Revises: 41f6a59a61f2 +Create Date: 2016-09-09 17:39:57.846309 + +""" +from alembic import op +import sqlalchemy as sa + +# revision identifiers, used by Alembic. +revision = '5e4a03ef0bf0' +down_revision = '41f6a59a61f2' + + +def upgrade(): + op.create_table( + 'access_request', + sa.Column('created_on', sa.DateTime(), nullable=True), + sa.Column('changed_on', sa.DateTime(), nullable=True), + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('table_id', sa.Integer(), nullable=True), + sa.Column('druid_datasource_id', sa.Integer(), nullable=True), + sa.Column('changed_by_fk', sa.Integer(), nullable=True), + sa.Column('created_by_fk', sa.Integer(), nullable=True), + sa.ForeignKeyConstraint(['changed_by_fk'], ['ab_user.id'], ), + sa.ForeignKeyConstraint(['created_by_fk'], ['ab_user.id'], ), + sa.ForeignKeyConstraint(['druid_datasource_id'], + ['datasources.id'], ), + sa.ForeignKeyConstraint(['table_id'], ['tables.id'], ), + sa.PrimaryKeyConstraint('id') + ) + + +def downgrade(): + op.drop_table('access_request') diff --git a/caravel/models.py b/caravel/models.py index eaa9ec3f9dbd8..06664efac451a 100644 --- a/caravel/models.py +++ b/caravel/models.py @@ -26,6 +26,7 @@ from flask_appbuilder import Model from flask_appbuilder.models.mixins import AuditMixin from flask_appbuilder.models.decorators import renders +from flask_appbuilder.security.sqla.models import Role, PermissionView from flask_babel import lazy_gettext as _ from pydruid.client import PyDruid @@ -699,6 +700,10 @@ def perm(self): "[{obj.database}].[{obj.table_name}]" "(id:{obj.id})").format(obj=self) + @property + def name(self): + return self.table_name + @property def full_name(self): return "[{obj.database}].[{obj.table_name}]".format(obj=self) @@ -1203,6 +1208,10 @@ def refresh_datasources(self): def perm(self): return "[{obj.cluster_name}].(id:{obj.id})".format(obj=self) + @property + def perm(self): + return "[{obj.cluster_name}].(id:{obj.id})".format(obj=self) + class DruidDatasource(Model, AuditMixinNullable, Queryable): @@ -1997,6 +2006,7 @@ def to_dict(self): 'tempTable': self.tmp_table_name, 'userId': self.user_id, } + @property def name(self): ts = datetime.now().isoformat() @@ -2004,3 +2014,66 @@ def name(self): tab = self.tab_name.replace(' ', '_').lower() if self.tab_name else 'notab' tab = re.sub(r'\W+', '', tab) return "sqllab_{tab}_{ts}".format(**locals()) + + +ROLES_BLACKLIST = set(['Admin', 'Alpha', 'Gamma', 'Public']) + + +class DatasourceAccessRequest(Model, AuditMixinNullable): + """ORM model for the access requests for datasources and dbs.""" + __tablename__ = 'access_request' + id = Column(Integer, primary_key=True) + + table_id = Column(Integer, ForeignKey('tables.id')) + druid_datasource_id = Column(Integer, ForeignKey('datasources.id')) + + table = relationship('SqlaTable', foreign_keys=[table_id]) + druid_datasource = relationship( + 'DruidDatasource', foreign_keys=[druid_datasource_id]) + + @property + def username(self): + return self.creator() + + @property + def datasource(self): + if self.druid_datasource: + return self.druid_datasource + return self.table + + @property + def datasource_url(self): + return self.datasource.link + + @property + def permission_view(self): + return sm.find_permission_view_menu( + 'datasource_access', self.datasource.perm) + + @property + def roles_with_datasource(self): + action_list = '' + for r in self.permission_view.role: + if r.name in ROLES_BLACKLIST: + continue + url = ( + '/caravel/approve?request_access_id={}&role_to_grant={}' + .format(self.id, r.name) + ) + href = 'Grant {} Role'.format(url, r.name) + action_list = action_list + '
  • ' + href + '
  • ' + return '' + + @property + def user_roles(self): + action_list = '' + for r in self.created_by.roles: + url = ( + '/caravel/approve?request_access_id={}&role_to_extend={}' + .format(self.id, r.name) + ) + href = 'Extend {} Role'.format(url, r.name) + if r.name in ROLES_BLACKLIST: + href = "{} Role".format(r.name) + action_list = action_list + '
  • ' + href + '
  • ' + return '' diff --git a/caravel/templates/caravel/request_access.html b/caravel/templates/caravel/request_access.html new file mode 100644 index 0000000000000..5ccef56800957 --- /dev/null +++ b/caravel/templates/caravel/request_access.html @@ -0,0 +1,24 @@ +{% extends "caravel/basic.html" %} +{% block title %}{{ _("No Access!") }}{% endblock %} +{% block body %} + {% include "caravel/flash_wrapper.html" %} +

    + {{ _("You do not have permissions to access the datasource %(name)s.", + name=datasource_name) + }} +

    +
    +
    + + +
    +
    +{% endblock %} \ No newline at end of file diff --git a/caravel/views.py b/caravel/views.py index 1e842a4f80a9e..4454c72e77f5e 100755 --- a/caravel/views.py +++ b/caravel/views.py @@ -81,13 +81,9 @@ def get_database_access_error_msg(database_name): "`all_datasource_access` permission", name=database_name) -def get_datasource_access_error_msg(datasource): - error = ("This endpoint requires the datasource %(name)s, database or " - "`all_datasource_access` permission") - if hasattr(datasource, 'table_name'): - return __(error, name=datasource.table_name) - else: - return __(error, name=datasource.datasource_name) +def get_datasource_access_error_msg(datasource_name): + return __("This endpoint requires the datasource %(name)s, database or " + "`all_datasource_access` permission", name=datasource_name) def get_error_msg(): @@ -628,6 +624,32 @@ def post_update(self, table): icon='fa-table',) +class AccessRequestsModelView(CaravelModelView, DeleteMixin): + datamodel = SQLAInterface(models.DatasourceAccessRequest) + list_columns = [ + 'username', 'user_roles', 'datasource_url', + 'roles_with_datasource', 'created_on'] + order_columns = [ + 'username', 'datasource_url'] + base_order = ('changed_on', 'desc') + label_columns = { + 'username': _("User"), + 'user_roles': _("User Roles"), + 'database': _("Database URL"), + 'datasource_url': _("Datasource"), + 'roles_with_datasource': _("Roles to grant"), + 'created_on': _("Created On"), + } + +appbuilder.add_view( + AccessRequestsModelView, + "Access requests", + label=__("Access requests"), + category="Security", + category_label=__("Security"), + icon='fa-table',) + + appbuilder.add_separator("Sources") @@ -966,6 +988,102 @@ def msg(self): class Caravel(BaseCaravelView): """The base views for Caravel!""" + @log_this + @has_access + @expose("/request_access_form///" + "") + def request_access_form( + self, datasource_type, datasource_id, datasource_name): + request_access_url = ( + '/caravel/request_access?{}={}&datasource_name={}'.format( + datasource_type, datasource_id, datasource_name) + ) + return self.render_template( + 'caravel/request_access.html', + request_access_url=request_access_url, + datasource_name=datasource_name, + slicemodelview_link='/slicemodelview/list/') + + @log_this + @has_access + @expose("/request_access") + def request_access(self): + table_id = request.args.get('table') + druid_datasource_id = request.args.get('druid') + datasource_name = request.args.get('datasource_name') + + duplicates = ( + db.session.query(models.DatasourceAccessRequest) + .filter( + models.DatasourceAccessRequest.created_by_fk == + g.user.get_id(), + models.DatasourceAccessRequest.table_id == table_id, + models.DatasourceAccessRequest.druid_datasource_id == + druid_datasource_id, + ) + ).all() + + if len(duplicates) > 0: + flash(__( + "You have already requested access to the datasource %(name)s", + name=datasource_name), "warning") + return redirect('/slicemodelview/list/') + + access_request = models.DatasourceAccessRequest( + table_id=table_id, + druid_datasource_id=druid_datasource_id, + ) + db.session.add(access_request) + db.session.commit() + flash(__("Access to the datasource %(name)s was requested", + name=datasource_name), "info") + return redirect('/slicemodelview/list/') + + @log_this + @has_access + @expose("/approve") + def approve(self): + request_access_id = request.args.get('request_access_id') + role_to_grant = request.args.get('role_to_grant') + role_to_extend = request.args.get('role_to_extend') + access_request = ( + db.session.query(models.DatasourceAccessRequest) + .filter_by(id=request_access_id).first() + ) + # check if you can approve + if (self.all_datasource_access() or + (access_request.datasource and + access_request.datasource.created_by_fk and + g.user.id == access_request.datasource.owner_id)): + # can by done by admin only + if role_to_grant: + role = sm.find_role(role_to_grant) + access_request.created_by.roles.append(role) + flash(__( + "%(user)s was granted the role %(role)s that gives access " + "to the %(datasource)s", + user=access_request.creator(), + role=role_to_grant, + datasource=access_request.datasource.full_name), "info") + + if role_to_extend: + sm.add_permission_role(sm.find_role(role_to_extend), + access_request.permission_view) + flash(__("Role %(role)s was extended to provide the access to" + " the datasource %(datasource)s", + role=role_to_extend, + datasource=access_request.datasource.full_name), + "info") + + else: + flash(__("You have no permission to approve this request"), + "danger") + return redirect('/accessrequestsmodelview/list/') + + db.session.delete(access_request) + db.session.commit() + return redirect('/accessrequestsmodelview/list/') + @has_access @expose("/explore////") @expose("/explore///") @@ -974,12 +1092,7 @@ class Caravel(BaseCaravelView): def explore(self, datasource_type, datasource_id, slice_id=None): error_redirect = '/slicemodelview/list/' datasource_class = src_registry.sources[datasource_type] - - datasources = ( - db.session - .query(datasource_class) - .all() - ) + datasources = db.session.query(datasource_class).all() datasources = sorted(datasources, key=lambda ds: ds.full_name) datasource = [ds for ds in datasources if int(datasource_id) == ds.id] datasource = datasource[0] if datasource else None @@ -989,8 +1102,10 @@ def explore(self, datasource_type, datasource_id, slice_id=None): return redirect(error_redirect) if not self.datasource_access(datasource): - flash(__(get_datasource_access_error_msg(datasource)), "danger") - return redirect(error_redirect) + flash( + __(get_datasource_access_error_msg(datasource.name)), "danger") + return redirect('caravel/request_access_form/{}/{}/{}'.format( + datasource_type, datasource_id, datasource.name)) request_args_multi_dict = request.args # MultiDict @@ -1561,7 +1676,7 @@ def select_star(self, database_id, table_name): # Prevent exposing column fields to users that cannot access DB. if not self.datasource_access(t.perm): - flash(get_datasource_access_error_msg(t), 'danger') + flash(get_datasource_access_error_msg(t.name), 'danger') return redirect("/tablemodelview/list/") fields = ", ".join( diff --git a/tests/base_tests.py b/tests/base_tests.py index 4cae61bf12157..653f22195ca1b 100644 --- a/tests/base_tests.py +++ b/tests/base_tests.py @@ -10,7 +10,7 @@ from flask_appbuilder.security.sqla import models as ab_models import caravel -from caravel import app, db, models, utils, appbuilder +from caravel import app, db, models, utils, appbuilder, sm os.environ['CARAVEL_CONFIG'] = 'tests.caravel_test_config' @@ -46,6 +46,27 @@ def __init__(self, *args, **kwargs): appbuilder.sm.find_role('Alpha'), password='general') + # create druid cluster and druid datasources + session = db.session + cluster = session.query(models.DruidCluster).filter_by( + cluster_name="druid_test").first() + if not cluster: + cluster = models.DruidCluster(cluster_name="druid_test") + session.add(cluster) + session.commit() + + druid_datasource1 = models.DruidDatasource( + datasource_name='druid_ds_1', + cluster_name='druid_test' + ) + session.add(druid_datasource1) + druid_datasource2 = models.DruidDatasource( + datasource_name='druid_ds_2', + cluster_name='druid_test' + ) + session.add(druid_datasource2) + session.commit() + utils.init(caravel) def login(self, username='admin', password='general'): @@ -71,6 +92,43 @@ def get_latest_query(self, sql): session.close() return query + def get_access_requests( + self, username, table_id=None, druid_datasource_id=None): + if table_id: + return ( + db.session.query(models.DatasourceAccessRequest) + .filter( + models.DatasourceAccessRequest.created_by_fk == + sm.find_user(username).id, + models.DatasourceAccessRequest.table_id == table_id + ).all() + ) + if druid_datasource_id: + return ( + db.session.query(models.DatasourceAccessRequest) + .filter( + models.DatasourceAccessRequest.created_by_fk == + sm.find_user(username).id, + models.DatasourceAccessRequest.druid_datasource_id == + druid_datasource_id + ).all() + ) + + + def get_table_by_name(self, table_name): + return ( + db.session.query(models.SqlaTable) + .filter(models.SqlaTable.table_name == table_name) + .first() + ) + + def get_druid_ds_by_name(self, druid_ds_name): + return ( + db.session.query(models.DruidDatasource) + .filter(models.DruidDatasource.datasource_name == druid_ds_name) + .first() + ) + def logout(self): self.client.get('/logout/', follow_redirects=True) diff --git a/tests/core_tests.py b/tests/core_tests.py index 2b24357997016..d1ab032edcce3 100644 --- a/tests/core_tests.py +++ b/tests/core_tests.py @@ -25,7 +25,6 @@ BASE_DIR = app.config.get("BASE_DIR") cli = imp.load_source('cli', BASE_DIR + "/bin/caravel") - class CoreTests(CaravelTestCase): def __init__(self, *args, **kwargs): @@ -45,7 +44,7 @@ def setUpClass(cls): def setUp(self): db.session.query(models.Query).delete() - + db.session.query(models.DatasourceAccessRequest).delete() def tearDown(self): pass @@ -161,7 +160,8 @@ def test_shortner(self): "flt_col_0=source&flt_op_0=in&flt_eq_0=&slice_id=78&slice_name=" "Energy+Sankey&collapsed_fieldsets=&action=&datasource_name=" "energy_usage&datasource_id=1&datasource_type=table&" - "previous_viz_type=sankey") + "previous_viz_type=sankey" + ) resp = self.client.post('/r/shortner/', data=data) assert '/r/' in resp.data.decode('utf-8') @@ -210,6 +210,225 @@ def test_add_slices(self, username='admin'): assert new_slice in dash.slices assert len(set(dash.slices)) == len(dash.slices) + def test_approve(self): + session = db.session + sm.add_role('table_role') + self.login('admin') + + def prepare_request(ds_name, role, lookup_function): + ds = lookup_function(ds_name) + ds_id = ds.id + ds_perm_view = sm.find_permission_view_menu( + 'datasource_access', ds.perm) + sm.add_permission_role(sm.find_role(role), ds_perm_view) + access_request = models.DatasourceAccessRequest( + table_id=ds_id, + created_by_fk=sm.find_user(username='gamma').id, + ) + session.add(access_request) + session.commit() + return access_request + + # Case 1. Grant new role to the user. + + access_request1 = prepare_request( + 'unicode_test', 'table_role', self.get_table_by_name) + self.client.get( + '/caravel/approve?request_access_id={}&role_to_grant={}' + .format(access_request1.id, 'table_role') + ) + access_requests = self.get_access_requests( + 'gamma', table_id=access_request1.table_id) + # request was removed + self.assertFalse(access_requests) + # user was granted table_role + user_roles = [r.name for r in sm.find_user('gamma').roles] + self.assertIn('table_role', user_roles) + + # Case 2. Extend the role to have access to the table + + access_request2 = prepare_request( + 'long_lat', 'table_role', self.get_table_by_name) + long_lat_perm = access_request2.table.perm + + self.client.get( + '/caravel/approve?request_access_id={}&role_to_extend={}' + .format(access_request2.id, 'table_role') + ) + access_requests = self.get_access_requests( + 'gamma', table_id=access_request2.table_id) + # request was removed + self.assertFalse(access_requests) + # table_role was extended to grant access to the long_lat table/ + table_role = sm.find_role('table_role') + perm_view = sm.find_permission_view_menu( + 'datasource_access', long_lat_perm) + self.assertIn(perm_view, table_role.permissions) + + # Case 3. Grant new role to the user to access the druid datasource. + + sm.add_role('druid_role') + access_request3 = prepare_request( + 'druid_ds_1', 'druid_role', self.get_druid_ds_by_name) + self.client.get( + '/caravel/approve?request_access_id={}&role_to_grant={}' + .format(access_request3.id, 'druid_role') + ) + + # user was granted table_role + user_roles = [r.name for r in sm.find_user('gamma').roles] + self.assertIn('druid_role', user_roles) + + # Case 4. Extend the role to have access to the druid datasource + + access_request4 = prepare_request( + 'druid_ds_2', 'druid_role', self.get_druid_ds_by_name) + druid_ds_2_perm = access_request4.datasource.perm + + self.client.get( + '/caravel/approve?request_access_id={}&role_to_extend={}' + .format(access_request4.id, 'druid_role') + ) + # druid_role was extended to grant access to the druid_access_ds_2 + druid_role = sm.find_role('druid_role') + perm_view = sm.find_permission_view_menu( + 'datasource_access', druid_ds_2_perm) + self.assertIn(perm_view, druid_role.permissions) + + # cleanup + gamma_user = sm.find_user(username='gamma') + gamma_user.roles.remove(sm.find_role('druid_role')) + gamma_user.roles.remove(sm.find_role('table_role')) + session.delete(sm.find_role('druid_role')) + session.delete(sm.find_role('table_role')) + session.commit() + + def test_request_access(self): + session = db.session + self.login(username='gamma') + gamma_user = sm.find_user(username='gamma') + sm.add_role('dummy_role') + gamma_user.roles.append(sm.find_role('dummy_role')) + session.commit() + + # Case 1. Request table access, there are no roles have this table. + + table1 = self.get_table_by_name('random_time_series') + table_id1 = table1.id + table1_perm = table1.perm + + # request access to the table + self.client.get('/caravel/request_access?table={}'.format(table_id1)) + + access_request1 = self.get_access_requests( + 'gamma', table_id=table_id1)[0] + self.assertEqual( + access_request1.user_roles, + '' + .format(access_request1.id)) + self.assertEqual(access_request1.roles_with_datasource, '
      ') + self.assertEqual( + access_request1.permission_view, + sm.find_permission_view_menu('datasource_access', table1_perm)) + + # Case 2. Duplicate request. + + self.client.get('/caravel/request_access?table={}'.format(table_id1)) + access_requests_2 = self.get_access_requests( + 'gamma', table_id=table_id1) + self.assertEqual(len(access_requests_2), 1) + + # Case 3. Request access, roles exist that contains the table. + + # add table to the existing roles + table3 = self.get_table_by_name('energy_usage') + table_3_id = table3.id + table3_perm = table3.perm + + sm.add_role('energy_usage_role') + alpha_role = sm.find_role('Alpha') + sm.add_permission_role( + alpha_role, + sm.find_permission_view_menu('datasource_access', table3_perm)) + sm.add_permission_role( + sm.find_role("energy_usage_role"), + sm.find_permission_view_menu('datasource_access', table3_perm)) + session.commit() + + self.client.get('/caravel/request_access?table={}'.format(table_3_id)) + + access_request3 = self.get_access_requests( + 'gamma', table_id=table_3_id)[0] + self.assertEqual( + access_request3.permission_view, + sm.find_permission_view_menu('datasource_access', table3_perm)) + self.assertEqual( + access_request3.roles_with_datasource, + ''.format(access_request3.id)) + + # Case 4. Request druid access, there are no roles have this table. + druid_ds_4 = self.get_druid_ds_by_name('druid_ds_1') + druid_ds_4_id = druid_ds_4.id + druid_ds_4_perm = druid_ds_4.perm + + # request access to the table + self.client.get( + '/caravel/request_access?druid={}'.format(druid_ds_4_id)) + + access_request4 = self.get_access_requests( + 'gamma', druid_datasource_id=druid_ds_4_id)[0] + self.assertEqual( + access_request4.user_roles, + '' + .format(access_request4.id)) + + self.assertEqual( + access_request4.roles_with_datasource, + '
        '.format(access_request4.id)) + self.assertEqual( + access_request4.permission_view, + sm.find_permission_view_menu('datasource_access', druid_ds_4_perm)) + + # Case 5. Roles exist that contains the druid datasource. + # add druid ds to the existing roles + druid_ds_5 = self.get_druid_ds_by_name('druid_ds_2') + druid_ds_5_id = druid_ds_5.id + druid_ds_5_perm = druid_ds_5.perm + + druid_ds_2_role = sm.add_role('druid_ds_2_role') + admin_role = sm.find_role('Admin') + sm.add_permission_role( + admin_role, + sm.find_permission_view_menu('datasource_access', druid_ds_5_perm)) + sm.add_permission_role( + druid_ds_2_role, + sm.find_permission_view_menu('datasource_access', druid_ds_5_perm)) + session.commit() + + self.client.get( + '/caravel/request_access?druid={}'.format(druid_ds_5_id)) + + access_request5 = self.get_access_requests( + 'gamma', druid_datasource_id=druid_ds_5_id)[0] + self.assertEqual( + access_request5.permission_view, + sm.find_permission_view_menu('datasource_access', druid_ds_5_perm)) + self.assertEqual( + access_request5.roles_with_datasource, + ''.format(access_request5.id)) + + # cleanup + gamma_user = sm.find_user(username='gamma') + gamma_user.roles.remove(sm.find_role('dummy_role')) + session.commit() def test_druid_sync_from_config(self): cluster = models.DruidCluster(cluster_name="new_druid") From 9122d057bb9d0948c295a226f26e48e4806ce318 Mon Sep 17 00:00:00 2001 From: Bogdan Kyryliuk Date: Mon, 19 Sep 2016 18:32:45 -0700 Subject: [PATCH 2/4] Address the comments. --- .../5e4a03ef0bf0_add_request_access_model.py | 5 +- caravel/models.py | 14 ++--- caravel/templates/caravel/request_access.html | 12 ++-- caravel/utils.py | 62 ++++++++++++------- caravel/views.py | 13 ++-- tests/core_tests.py | 28 +++++++++ 6 files changed, 86 insertions(+), 48 deletions(-) diff --git a/caravel/migrations/versions/5e4a03ef0bf0_add_request_access_model.py b/caravel/migrations/versions/5e4a03ef0bf0_add_request_access_model.py index d505eef0d6d31..a872647f49b77 100644 --- a/caravel/migrations/versions/5e4a03ef0bf0_add_request_access_model.py +++ b/caravel/migrations/versions/5e4a03ef0bf0_add_request_access_model.py @@ -10,7 +10,7 @@ # revision identifiers, used by Alembic. revision = '5e4a03ef0bf0' -down_revision = '41f6a59a61f2' +down_revision = '65903709c321' def upgrade(): @@ -25,8 +25,7 @@ def upgrade(): sa.Column('created_by_fk', sa.Integer(), nullable=True), sa.ForeignKeyConstraint(['changed_by_fk'], ['ab_user.id'], ), sa.ForeignKeyConstraint(['created_by_fk'], ['ab_user.id'], ), - sa.ForeignKeyConstraint(['druid_datasource_id'], - ['datasources.id'], ), + sa.ForeignKeyConstraint(['druid_datasource_id'], ['datasources.id'], ), sa.ForeignKeyConstraint(['table_id'], ['tables.id'], ), sa.PrimaryKeyConstraint('id') ) diff --git a/caravel/models.py b/caravel/models.py index 06664efac451a..878de877042ca 100644 --- a/caravel/models.py +++ b/caravel/models.py @@ -1204,9 +1204,6 @@ def refresh_datasources(self): for datasource in self.get_datasources(): if datasource not in config.get('DRUID_DATA_SOURCE_BLACKLIST'): DruidDatasource.sync_to_db(datasource, self) - @property - def perm(self): - return "[{obj.cluster_name}].(id:{obj.id})".format(obj=self) @property def perm(self): @@ -2016,9 +2013,6 @@ def name(self): return "sqllab_{tab}_{ts}".format(**locals()) -ROLES_BLACKLIST = set(['Admin', 'Alpha', 'Gamma', 'Public']) - - class DatasourceAccessRequest(Model, AuditMixinNullable): """ORM model for the access requests for datasources and dbs.""" __tablename__ = 'access_request' @@ -2031,6 +2025,8 @@ class DatasourceAccessRequest(Model, AuditMixinNullable): druid_datasource = relationship( 'DruidDatasource', foreign_keys=[druid_datasource_id]) + ROLES_BLACKLIST = set(['Admin', 'Alpha', 'Gamma', 'Public']) + @property def username(self): return self.creator() @@ -2042,7 +2038,7 @@ def datasource(self): return self.table @property - def datasource_url(self): + def datasource_link(self): return self.datasource.link @property @@ -2054,7 +2050,7 @@ def permission_view(self): def roles_with_datasource(self): action_list = '' for r in self.permission_view.role: - if r.name in ROLES_BLACKLIST: + if r.name in self.ROLES_BLACKLIST: continue url = ( '/caravel/approve?request_access_id={}&role_to_grant={}' @@ -2073,7 +2069,7 @@ def user_roles(self): .format(self.id, r.name) ) href = 'Extend {} Role'.format(url, r.name) - if r.name in ROLES_BLACKLIST: + if r.name in self.ROLES_BLACKLIST: href = "{} Role".format(r.name) action_list = action_list + '
      • ' + href + '
      • ' return '
          ' + action_list + '
        ' diff --git a/caravel/templates/caravel/request_access.html b/caravel/templates/caravel/request_access.html index 5ccef56800957..0c075bac23ddc 100644 --- a/caravel/templates/caravel/request_access.html +++ b/caravel/templates/caravel/request_access.html @@ -2,12 +2,12 @@ {% block title %}{{ _("No Access!") }}{% endblock %} {% block body %} {% include "caravel/flash_wrapper.html" %} -

        - {{ _("You do not have permissions to access the datasource %(name)s.", - name=datasource_name) - }} -

        -
        +
        +

        + {{ _("You do not have permissions to access the datasource %(name)s.", + name=datasource_name) + }} +