-
Notifications
You must be signed in to change notification settings - Fork 0
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
Assign HQ-defined roles to CCA user #59
Conversation
Add menu permissions and do small refactor
Fix tests
Hey @Charl1996 Can you link the related HQ PR in the description please? |
Done |
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.
@@ -163,6 +144,26 @@ def sync_domain_role(self, domain): | |||
self.sm.get_session.add(current_user) | |||
self.sm.get_session.commit() | |||
|
|||
@staticmethod |
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.
💯
@@ -56,6 +56,15 @@ def set_oauth_session(self, provider, oauth_response): | |||
# } | |||
session[SESSION_OAUTH_RESPONSE_KEY] = oauth_response | |||
|
|||
def set_role_permissions(self, role, permissions): |
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.
nit: its confusing that this is in the OAuth file. Isn't there a better place for this addition?
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 was also thinking it feels a little out of place, but then all the permissions-related functionality sits in the class this class inherits from (methods like add_role
, add_view_menu_permission
, etc.), and since this is a permissions-related method I thought it's probably best putting it here. Any other suggestions are welcome.
(This class is the security manager, which handles the roles, permissions etc.)
CAN_DELETE_PERMISSIONS, | ||
] | ||
|
||
READ_ONLY_MENU_PERMISSIONS = { |
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.
As I read this now, I believe at certain point we could see HQ setting read specifically for either of Chart, Dataset, Dashboard etc.
It does appear that, the way this is currently structured, that should be feasible. Right?
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.
Correct. I played around a lot with how to best separate the read and write whilst keeping duplication to a minimum, but this current way of doing it feels like the most granular and configurable approach. You can set specific permissions for specific view/menu's.
# Any other additional roles to be assigned to the user on top of the base role | ||
# Note: by design we cannot use AUTH_USER_REGISTRATION_ROLE to | ||
# specify more than one role | ||
AUTH_USER_ADDITIONAL_ROLES = ["sql_lab"] |
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 @Charl1996
How do we roll this change out? Is there anything specifically needed for existing users? or their permission would reset correctly when they login.
So, will this role get removed from their account automatically now, if they had it earlier?
How does platform role work along with domain role? If someone gets the role sql_lab, it would be applicable only for the project they are currently on? and when they switch project, their platform roles are fetched and updated?
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.
or their permission would reset correctly when they login.
Yes, when they log in their permissions will reset correctly.
So, will this role get removed from their account automatically now, if they had it earlier?
Correct. My thoughts are that if a user needs access they need to now have it explicitly granted on HQ (even if it means they might not have that permissions until it's explicitly given to them on HQ if they had it before), but maybe I can check with AEs as well.
How does platform role work along with domain role? If someone gets the role sql_lab, it would be applicable only for the project they are currently on?
Ah! Brilliant question! See response below.
and when they switch project, their platform roles are fetched and updated?
More accurately, fetched and overwritten.
With the way it works in this PR, when a user logs in their user on CCA will be assigned roles/permissions based on only the domain's configuration they logged in to, so if their user on that domain on HQ does not have the Gamma role for instance, the user on CCA will not be granted that role when logging in. This is true even though the user might have Gamma on another domain; when they log into that other domain they will be given the Gamma role. So even though the platform role is technically platform wide, the user will only ever be able to have that role on the domain which gives it to the user. This is true because every time a user logs in we overwrite the user's roles completely with the appropriate roles for the domain the user logs into.
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.
Correct. My thoughts are that if a user needs access they need to now have it explicitly granted on HQ (even if it means they might not have that permissions until it's explicitly given to them on HQ if they had it before), but maybe I can check with AEs as well.
Right. It seems if we roll this out, people would lose access, at least temporarily, till they are assigned the permission on HQ.
The good thing is that the permissions setup is already available on HQ. So we can ask projects to set them even before we release this.
Additionally, we should be explicit that the permissions are updated only at login and when switching domains so they need to re-login to see the updated permissions from HQ.
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.
permissions are updated only at login
This is not entirely true; the permissions are updated when selecting a domain, so even though the user is "logged in" to superset, when they change domains the permissions will update (sorry, maybe I wasn't too clear on this distinction in my previous comment)
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.
Right, that is what I meant by "only at login and when switching domains"?
may be a correction to that "they need to re-login or re-select their domain" (I don't know if they can do that, if they have only one domain, which would be for most users?)
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.
Oh! Sorry, yeah, now I understand what you meant. You're right. Although there is a way when logged into the app to go to the "select domains" page without having to explicitly log out and re-auth.
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.
Cool.
And we need to plan for the rollout?
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've checked with AEs and it's OK that users might loose temporary access if their HQ permissions isn't set up correctly; we just have to make sure to communicate this change when deploying the new changes.
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.
Cool.
Yes. I updated the description to make it more visible. |
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.
looking forward to QA on this.
hq_superset/hq_url.py
Outdated
@@ -32,5 +32,5 @@ def datasource_unsubscribe(domain, datasource_id): | |||
) | |||
|
|||
|
|||
def user_domain_roles(username, domain): |
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.
HQ will now get the user from the request.
1. Users with 'edit' permissions gets the superset Gamma role 2. If HQ user don't have view permissions, don't allow access
Please note that an update has been made which basically does the following:
|
hq_superset/views.py
Outdated
DomainSyncUtil(superset.appbuilder.sm).sync_domain_role(hq_domain) | ||
if not DomainSyncUtil(superset.appbuilder.sm).sync_domain_role(hq_domain): | ||
flash( | ||
"You don't have the necessary HQ permissions to access this domain.", |
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.
nit: might be useful to add the name of the domain to the message so the user knows which domain they were trying to access?
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.
Good idea!
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.
hq_superset/utils.py
Outdated
additional_roles = [] | ||
if domain_permissions[CAN_WRITE_PERMISSION]: | ||
platform_roles_names.append( | ||
'gamma', |
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.
should this be the one with "G"? The one on superset is "Gamma" or "gamma"?
Extracted it to a constant would be helpful to avoid confusion
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.
Extracted it to a constant would be helpful to avoid confusion
Good idea
hq_superset/utils.py
Outdated
|
||
@staticmethod | ||
def _domain_user_role_name(domain, user): | ||
return f"{domain}_user_{user.id}_read_only" |
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 @Charl1996
I wanted to check with you if its possible to add the HQ role UUID to the name?
While looking at the ticket to expire permissions after every hour, I realized we currently don't a way of linking HQ role to the CCA role, which could be useful.
If its not a big lift, would be good to do it now since we are releasing this now.
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 don't think it would be that much of a big lift. I'll look into it.
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.
Awesome, thank you!
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.
@mkangia I had a look into this and hit a snag...the Admin role is a virtual role and is never persisted in the database, so if the user is admin we don't have a role ID that we could use.
Maybe we should rethink how we track the role linkage. I'm going to hold off on this for now.
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.
Actually, thinking of it, why do we need to keep track of the role? We know who the user is and we know the domain, so we can just check if the user has CCA access on the domain (which is what we do on log-in to a specific domain).
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.
So whenever it's needed to sync the role again you should just be able to call DomainSyncUtil.sync_domain_role(domain)
and the rest will sort itself out.
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 the user is admin we don't have a role ID that we could use.
so the id could be "admin"?
I'm going to hold off on this for now.
We don't need to block on this. Fine to go ahead without the role id if needed.
All looks good here except this point that I believe will add redundant roles in CCA |
Hey @Charl1996 Can you confirm what happens if HQ does not respond well or say responds with a 500 when fetching roles/permissions? |
@mkangia Here CCA simply returns empty permissions if the status is not I'm curious at what point you got an 500? If HQ returned it, you should have seen the error message, but if CCA have thrown it I think you should see the 500 status page. |
Hey @Charl1996 I think the error message is a bit misleading thought. May be we could update to tell the user that we could not sync their permission instead of saying that they don't have permission. |
If we merge this PR whilst keeping the resource on |
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 @Charl1996
Seems good to go ahead with this now.
Suggested some improvements, that are fine to taken as a follow up even as a separate ticket, if any of them seem reasonable. Considering the approach got simpler, which is great, code/implementation can be simplified further.
CAN_WRITE_PERMISSION = "can_write" | ||
CAN_EDIT_PERMISSION = "can_edit" | ||
CAN_ADD_PERMISSION = "can_add" | ||
CAN_DELETE_PERMISSIONS = "can_delete" |
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.
it seems some of these are now redundant and can be removed.
|
||
|
||
def user_domain_roles(domain): | ||
return f"a/{domain}/api/analytics-roles/v1" |
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.
nit: can we add the trailing slash for consistency?
The user gets assigned at least 3 roles in order to function on any domain: | ||
1. hq_user_role: gives access to superset platform | ||
2. domain_schema_role: restricts user access to specific domain schema | ||
3. domain_user_role: restricts access for particular user on domain in accordance with how the permissions |
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 believe this needs to be updated now? we don't have a domain_user_role anymore right?
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.
Can we add to the documentation what return value from this function means? i.e when does it return False/True?
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, good catch!
This role is the bare minimum required for a user to be able to have an account on | ||
superset | ||
""" | ||
hq_user_role = self.sm.add_role(HQ_USER_ROLE_NAME) |
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.
nit: I wonder if this is causing unnecessary saves on this role in the db since it would exist once it has been created.
Non blocking but good to check and do a follow up if needed.
I was thinking if this should instead be added by AUTH_USER_ADDITIONAL_ROLES instead though I am not sure how that would work since its a custom role and not predefined in superset.
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.
Mmm...I'll check it out
] | ||
self.sm.set_role_permissions(hq_user_role, hq_user_base_permissions) | ||
|
||
if hq_user_role not in current_user.roles: |
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.
nit: should this check be on the top so the role isn't set if its already present?
# Map between HQ and CCA | ||
permissions = { | ||
CAN_WRITE_PERMISSION: hq_permissions["can_edit"], | ||
CAN_READ_PERMISSION: hq_permissions["can_view"], |
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.
nit: do we really need these mapping? we can just return permissions and that should do right?
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 guess you're right. Will update (in upcoming PR)
@@ -40,6 +40,8 @@ | |||
|
|||
|
|||
class HQDatasourceView(BaseSupersetView): | |||
class_permission_name = "ExploreFormDataRestApi" |
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.
just checking on why was this added?
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.
This was to ensure that only users with edit permissions can import data sources.
@@ -63,6 +65,8 @@ def _ucr_id_from_pk(self, datasource_pk): | |||
return None | |||
|
|||
@expose("/update/<datasource_id>", methods=["GET"]) | |||
@has_access | |||
@permission_name("write") |
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.
these checks were missing earlier?
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, but earlier we didn't need them. Now we need to set them to explicitly disable a read-only user from performing this action.
@mkangia I'm going to merge this just to move it forward, but will be addressing your (very valid) comments in a followup PR. |
|
Ticket
This PR expands on the DomainSyncUtil class's
sync_domain_role
method to now consider the roles for a particular user as defined on CommCare HQ. This PR is for returning the CCA roles for a particular user.Note: This work will go through QA and is already on staging.
Approach
The approach is slightly different than the spec suggests in the following ways:
By having only 1 role per domain user and setting the permissions for that role we won't end up having redundant roles that we'll need to clean up.
Side note
Since we're not assigning the
Gamma
by default to all users anymore (it's a configurable role on HQ now), it was found that the CCA user cannot log in to Superset since the privilege of having a profile on Superset is a permission the user needs. If the user does not have this permission, the user's browser will redirect endlessly resulting in a browser error.To account for this a new standard role, namely
hq_user
, is assigned to all CCA users, regardless of their permissions on CommCare HQ. This role has the necessary ("baseline") permissions needed for any users to be able have a profile on Superset/ be able to log in to Superset.This means that users will need at least 3 distinct roles if they want to be able to access a domain's data on Superset:
In addition to the above roles the user is able to have more roles if so defined on CommCare HQ. The roles available on HQ is currently
Gamma
,dataset_editor
andsql_lab
.