-
Notifications
You must be signed in to change notification settings - Fork 13.8k
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
Conversation
50e5d09
to
ec3e4e4
Compare
d1bbdaf
to
d700e01
Compare
superset/superset_rmv.py
Outdated
self.remove_dashboard_role(role, dash) | ||
|
||
|
||
class SupersetSecurityManager(SecurityManager): |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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): |
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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}))
…into dash-access-control
|
||
|
||
def upgrade(): | ||
op.create_table('dashboard_role', |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
@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 It's an important feature for extending superset in any not-purely-internal scenario |
This is a much needed feature. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
expecting this feature
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. |
|
||
|
||
def upgrade(): | ||
pass |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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): |
There was a problem hiding this comment.
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): |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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): |
There was a problem hiding this comment.
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.
@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. |
Yes please close this one 😸 |
closing, in favor of #12680 |
Add permissions at the dashboard level.