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

Implement permission request/approve flow. #1095

Merged
merged 5 commits into from
Sep 22, 2016
Merged

Implement permission request/approve flow. #1095

merged 5 commits into from
Sep 22, 2016

Conversation

bkyryliuk
Copy link
Member

@bkyryliuk bkyryliuk commented Sep 12, 2016

This PR implements the basic request permissions flow: #1049
For the 1st iteration it is pretty simple flow:

  • permission requests exist on the user - datasource (table or druid) level
  • user is redirected to the request form if he/she doesn't have permissions
  • admins, alphas or datasource owners have permissions to approve the requests. only admins can grant roles to the users
  • the access request view is visible to admins only
  • there are 2 ways to grant the permission to the user, grant the user role that has access to the datasource, extend the role the user already has to provide access to the datasource
  • disallows multiple requests to the same datasource
  • extensive testing, it was quite tricky to make it working

Potential future improvements:

  • request access to the databases, not only to the datasources
  • include free form message in the request form
  • offer to create a role if there are 0 roles granting access to the given datasource
  • email notifications
  • batch approval
  • automatic clean up of the request if approving one of the requests resolves all others
  • store some metadata - dashboard / slide ids
  • better support for the dashboards - show the slices you have access to and allow to request access to others

Reviewers:

Screenshots

request_flow

approve_flow

@bkyryliuk bkyryliuk changed the title Work in progress. Implement permission request/approve flow. Implement permission request/approve flow. Sep 15, 2016
Copy link
Member

@mistercrunch mistercrunch left a comment

Choose a reason for hiding this comment

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

Looks neat!

name=datasource_name)
}}
</h4>
<div id="app">
Copy link
Member

Choose a reason for hiding this comment

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

no need for id="app" afaict

{% block title %}{{ _("No Access!") }}{% endblock %}
{% block body %}
{% include "caravel/flash_wrapper.html" %}
<h4>
Copy link
Member

Choose a reason for hiding this comment

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

wrap the whole thing in a <div class="container"> to get a nice left and right margin

@@ -1181,6 +1186,10 @@ def refresh_datasources(self):
if datasource not in config.get('DRUID_DATA_SOURCE_BLACKLIST'):
DruidDatasource.sync_to_db(datasource, self)

@property
Copy link
Member

Choose a reason for hiding this comment

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

oh this should now be in master now

return self.table

@property
def datasource_url(self):
Copy link
Member

Choose a reason for hiding this comment

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

Rename to datasource_link
I'd except a link to be a <a> and a url to be http://...

list_columns = [
'username', 'user_roles', 'datasource_url',
'roles_with_datasource', 'created_on']
order_columns = [
Copy link
Member

Choose a reason for hiding this comment

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

nit: would fit on one line

models.DatasourceAccessRequest.druid_datasource_id ==
druid_datasource_id,
)
).all()
Copy link
Member

Choose a reason for hiding this comment

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

NIT: it works here, but .all() could be aligned with .filter(

)
).all()

if len(duplicates) > 0:
Copy link
Member

Choose a reason for hiding this comment

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

Empty lists evals as False, so you can simply if duplicates:

.filter_by(id=request_access_id).first()
)
# check if you can approve
if (self.all_datasource_access() or
Copy link
Member

Choose a reason for hiding this comment

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

this endpoint and model related perms should be added to the admin role only (check the cil's init function), then we don't need anything else than @has_access.

Copy link
Member Author

Choose a reason for hiding this comment

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

Done and covered in the unit tests

"danger")
return redirect('/accessrequestsmodelview/list/')

db.session.delete(access_request)
Copy link
Member

Choose a reason for hiding this comment

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

Not needed but there could be a need to cleanup here. If I asked access to 12 tables and you grant a single role that gives access to all 8 tables, you could clean up the other 7 requests, but let's leave this out for v1.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yep, will do it in separate PR

@bkyryliuk
Copy link
Member Author

@mistercrunch - please take another look

Copy link
Member

@mistercrunch mistercrunch left a comment

Choose a reason for hiding this comment

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

Sorry for the "close to the finish line" changes in requirements, tell me what you think.

"danger")
return redirect('/accessrequestsmodelview/list/')

db.session.delete(access_request)
Copy link
Member

Choose a reason for hiding this comment

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

I should have caught this before, but it's critical that we keep an audit trail of who granted access to what/who/when. One way to do this is to not delete the request, and flag it as resolved instead (add a state field to the model, a fk to the userid who granted access, timestamp when it was granted, and maybe the resolution that was applied).

Another option is to store the operation in another model.

Copy link
Member Author

Choose a reason for hiding this comment

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

@log_this - tackles it now

).all()
)


Copy link
Member

Choose a reason for hiding this comment

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

nit: one too many black line

'Security',
'UserDBModelView',
'SQL Lab <span class="label label-danger">alpha</span>',
'AccessRequestsModelView',
Copy link
Member

Choose a reason for hiding this comment

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

We probably need a new role for people who can grant rights to others that are not admin. It doesn't have to be part of this PR, your call.

Copy link
Member Author

Choose a reason for hiding this comment

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

agree, separate PR

id = Column(Integer, primary_key=True)

table_id = Column(Integer, ForeignKey('tables.id'))
druid_datasource_id = Column(Integer, ForeignKey('datasources.id'))
Copy link
Member

Choose a reason for hiding this comment

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

we probably want to follow the new pattern of datasource_id and datasource_type that was introduced in the Yahoo PR

Copy link
Member Author

Choose a reason for hiding this comment

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

Updated

"""ORM model for the access requests for datasources and dbs."""
__tablename__ = 'access_request'
id = Column(Integer, primary_key=True)

Copy link
Member

Choose a reason for hiding this comment

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

related to my other comment about auditing who granted what to who, I'd add:

state as ('requested', 'granted' and maybe eventually 'denied')
processed_by_userid Integer
processed_on Datetime

Copy link
Member Author

Choose a reason for hiding this comment

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

I've refactored the code in a way that @log_this will have all needed information

@bkyryliuk
Copy link
Member Author

@mistercrunch - please take one more look on the PR, couple new features

  • cleans up all the user requests, not just 1 once the access to the datasource is granted1.
  • logs the requests and approves
  • follows new rules of the datasource access

flash(USER_MISSING_ERR, "alert")
return json_error_response(USER_MISSING_ERR)

requests = (session.query(models.DatasourceAccessRequest).filter(
Copy link
Member

@mistercrunch mistercrunch Sep 22, 2016

Choose a reason for hiding this comment

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

optional: indent is inconsistent with other queries, looks like maybe sublime auto-indented that.

Sometimes when I have long strings that get referenced a lot in a small section of code, I create a shortcut as in DAR = models.DatasourceAccessRequest.

Copy link
Member Author

Choose a reason for hiding this comment

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

Done, thanks

@mistercrunch
Copy link
Member

LGTM. This is an awesome feature, great work!

It's lowering friction around obtaining access, granting access, and addressing the lack of auditing around access, all this is super impactful.

Follow up, related items:

  • maintain a system role for people who can grant rights
  • document the flows in the security section of the docs
  • a quick way to see the columns in the table (tooltip? link to endpoint that list the columns for a table?)
  • it's not clear yet, but something around managing how granters could only grant permissions to a subset of dataset (this may be over-thinking it at this point)

@bkyryliuk bkyryliuk merged commit cbc70d3 into apache:master Sep 22, 2016
@dpgaspar dpgaspar mentioned this pull request Sep 5, 2019
12 tasks
@aleksarias
Copy link

Hello, I don't see any information on how to activate this feature. I don't see it in the latest release of superset, 0.37.2. Does anyone know if it can be activated in that version?

@aleksarias
Copy link

aleksarias commented Oct 23, 2020

Found the setting the full example config file: https://github.com/apache/incubator-superset/blob/master/superset/config.py

# Set this to false if you don't want users to be able to request/grant
# datasource access requests from/to other users.
ENABLE_ACCESS_REQUEST = False

@aleksarias
Copy link

After enabling this feature, I'm getting "NOT FOUND".
https://mydomain.com/superset/dashboard/46/superset/request_access/?dashboard_id=46&

Is there something else that needs to be activated in order to get this feature to work?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🏷️ bot A label used by `supersetbot` to keep track of which PR where auto-tagged with release labels 🚢 0.11.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants