Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dashboard level access control #5099

Closed
wants to merge 13 commits into from

Conversation

jasnovak
Copy link
Contributor

Add permissions at the dashboard level.

  • Create permissions for each dashboard with the format "dashboard access on [dashboard_title](dash_id:x)"
  • The name of the permission is updated if the dashboard title is changed.
  • Add a dashboard permission to a role via the Edit Role form, or, for convenience, add a role to a dashboard via the Edit Dashboard form.
  • Optionally, add a default role to be added to each new dashboard created from a slice.
  • The permission "all_dashboard_access" is added to Admin, Alpha, and Gamma roles.
  • To allow private dashboards, remove "all_dashboard_access" from a role. This role will then only have access to the dashboards specified. Add a dashboard's permission to a role to "publish" it.
  • Note that these permissions do not apply to slices. To make the data in a dashboard truly private, use dashboard permissions with a read only role that has no slice/explore access.

self.remove_dashboard_role(role, dash)


class SupersetSecurityManager(SecurityManager):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SupersetSecurityManager is already defined in security.py and is the base people should use to override security-related things in their environment

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, this code was left over from the merge (I had defined my own SupersetSecurityManager before it was added to security.py). I've removed it. This new rolemodelview is already defined in the class in security.py.

{d: True for d in self.get_view_menus('dashboard_access')}

dashboard_ids = []
for dash in db.session.query(Dash):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's concern in how getting the list of dashboards a user has access to is going to be an expensive operation.

Ideally here we want to really limit the number of round trips to the database (it should be one complex query really, if possible), as well as the complexity of the query (if the user has all_dashboard_access, the query shouldn't do complex join to the rbac tables)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I was concerned about that as well. But listing dashboards with this change runs at ~250ms for us, with several hundred of dashboards. I can look at limiting the round trips though.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm thinking here you want to only fetch the dashboard_perms if all_dashboard_access is not found, and then craft a filter(*sqla.or_(models.Dashboard.perm.in_(dashboard_perms), {pre-existing conditions here}))



def upgrade():
op.create_table('dashboard_role',
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey so the model so far has been to not create these many-to-many tables, and instead creating perms for each object (table, database, schema). This has pros/cons, one of the pros being to have the security model limited to FAB's RBAC tables. Meaning roles are only made out of users and perms. Then perms act as imperfect FKs to the object table. Cons is the fact that the join is imperfect as we want the perm object to be human readable, but contains the id.

I think I like the approach here, made possible by the fact that we now have SupersetSecurityManager and can specify rolemodelview. I can see how we'd move to do pattern for Databases, DruidDatasource, SqlaTable at some point. I'm not sure what others may think about this approach.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a fan of proper foreign keys in join tables over FKs in permisssion names - it makes the data model more explicit and we can more easily enforce data consistency.

@kristw kristw added enhancement:request Enhancement request submitted by anyone from the community .security labels Feb 7, 2019
@micimize
Copy link
Contributor

micimize commented Aug 1, 2019

@jasnovak @mistercrunch Should this be in PRs Status "Changes Requested"?

What needs to happen to get this merged? Can we just get it without the early all_dashboard_access check, and optimize later?

It's an important feature for extending superset in any not-purely-internal scenario

@jay-finn
Copy link

jay-finn commented Apr 8, 2020

This is a much needed feature.
@jasnovak @mistercrunch are there any plans to merge this soon?
Is there a workaround for this in Superset 0.35.1 to provide users access to specific dashboards?

Copy link

@kalimuthu123 kalimuthu123 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

expecting this feature

@bkyryliuk
Copy link
Member

It would be great to have some clarity on how will dashboard access work with schema / datasource access. Maybe update the description to cover those use cases.
Also will the user be able to explore from the dashboard if has dashboard access but not table access ?



def upgrade():
pass
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this migration is no-op, let's remove it before merge.



def upgrade():
pass
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same with this migration.

from flask_babel import lazy_gettext as _


class SupersetRoleModelView(RoleModelView):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's update the file name to remove the abbreviation (superset_role_model_view.py rather than superset_rmv.py. Clarity > Brevity.

@@ -266,6 +274,157 @@ def test_dashboard_with_created_by_can_be_accessed_by_public_users(self):

assert 'Births' in self.get_resp('/superset/dashboard/births/')

def test_dashboard_level_access_controls(self):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's break up this test. There's a lot here, and additional test methods would make it clearer what's being tested at each point.

security_manager.add_permission_role(gamma_role, perm_view)

# Gamma role has all_dashboard_access by default
# check that Gamma user can access all dashboards
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comments like this are a great place to break up test cases.

@@ -0,0 +1,62 @@
# -*- coding: utf-8 -*-
# pylint: disable=C,R,W
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please do not disable pylint on new files.

@@ -96,6 +96,13 @@ def get_datasource_access_error_msg(datasource_name):
'`all_datasource_access` permission', name=datasource_name)


def get_dashboard_access_error_msg(dashboard_title):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's put free-floating functions in utils.py - this module is already enormous.

@junlincc
Copy link
Member

@srinify and I are trying to clear some old PRs in the repo. @jasnovak believe that this PR is no longer active. some of the functionality is also covered by #12680 @amitmiran137.
Is it alright to close? Thanks for your contribution!

@amitmiran137
Copy link
Member

Yes please close this one 😸

@junlincc
Copy link
Member

closing, in favor of #12680

@junlincc junlincc closed this Jan 31, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement:request Enhancement request submitted by anyone from the community
Projects
None yet
Development

Successfully merging this pull request may close these issues.

10 participants