-
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
Expire user permissions #72
Conversation
dc39da7
to
12a7dc0
Compare
- /api/v1/me is called frequently leading to before_request_hook getting fired unnecessarily, specifically for sync_user_domain_role leading to more than one syncs happening at once leading to db conflicts/errors
12a7dc0
to
c8111d4
Compare
@@ -48,3 +49,33 @@ def tearDown(self): | |||
sql = "; ".join(domain_schemas) + ";" | |||
connection.execute(text(sql)) | |||
super(HQDBTestCase, self).tearDown() | |||
|
|||
|
|||
class LoginUserTestMixin(object): |
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.
Nice. Seems useful.
hq_superset/utils.py
Outdated
@@ -415,3 +418,7 @@ def cast_data_for_table( | |||
def generate_secret(): | |||
alphabet = string.ascii_letters + string.digits | |||
return ''.join(secrets.choice(alphabet) for __ in range(64)) | |||
|
|||
|
|||
def datetime_utcnow_naive(): |
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 think this function name is a misnomer. You're returning an aware datetime, are you not?.
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.
You are right, I got that backwards, will rename this datetime_utcnow
.
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/hq_domain.py
Outdated
if not session.get(SESSION_DOMAIN_ROLE_LAST_SYNCED_AT): | ||
return True | ||
|
||
time_since_last_sync = datetime_utcnow_naive() - session[SESSION_DOMAIN_ROLE_LAST_SYNCED_AT] |
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
Nit: for more robust implementation we could check if session[SESSION_DOMAIN_ROLE_LAST_SYNCED_AT]
is a datetime-like object that could be operated on.
How about:
last_sync_datetime = session[SESSION_DOMAIN_ROLE_LAST_SYNCED_AT]
if not isinstance(last_sync_datetime, datetime):
return True
time_since_last_sync = datetime_utcnow_naive() - last_sync_datetime
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. I can add that. This value is only always set by CCA and can't edited by the user so it would certainly be a datetime but no harm in adding safety.
hq_superset/hq_domain.py
Outdated
def _sync_domain_role(): | ||
DomainSyncUtil(superset.appbuilder.sm).sync_domain_role(g.hq_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.
@mkangia
What happens if the domain role cannot sync during this routine? Is it fine to keep the user logged in in the current state?
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.
Ya, Great question.
I guess I will follow the same pattern you have followed otherwise in case sync domain role returns false and redirect user to the select domain page, though possibly with a different error message like "Your permissions have expired and the sync failed due to an unexpected error. Please select project space again or login to continue"
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 turned out to be a little challenging, but I got it working like this
a195929
@@ -134,7 +134,7 @@ def _do_assert(datasources): | |||
client.get('/hq_datasource/list/', follow_redirects=True) | |||
_do_assert(self.oauth_mock.test2_datasources) | |||
|
|||
@patch.object(DomainSyncUtil, "sync_domain_role", return_value=True) | |||
@patch.object(DomainSyncUtil, "_get_domain_access", return_value=({"can_write": True, "can_read": 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.
Curious what the issue was here?
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 was a flaky test that succeeded when run separately or if only this test file was run but failed when the full test suite was run.
I believe the permissions were getting set unexpectedly or getting revoked somehow, so I set them specifically instead of skipping it via the patch for sync_domain_role
. I did try to debug it for a couple of hours before letting it be.
@@ -44,6 +44,7 @@ def after_request_hook(response): | |||
'AuthOAuthView.login', | |||
'AuthOAuthView.logout', | |||
'AuthOAuthView.oauth_authorized', | |||
'CurrentUserRestApi.get_me', |
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.
Curious what this is for?
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.
From the UI, this a call made to constantly sync the user record, I assume its getting used in javascript/UI somewhere.
It is a domain independent API call.
I added it to this list so we don't fire the before request hooks on such requests since they are not needed.
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.
Found one more, b4eb1b3
@mkangia Nice! Thanks for the refactors and good tests! |
59370e1
to
a5f2c58
Compare
from .models import DataSetChange | ||
from .oauth2_server import authorization, require_oauth | ||
from .tasks import process_dataset_change | ||
from hq_superset.models import DataSetChange |
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 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.
Great Refactors. The changes looks good.
Just a query related to the fix made in the commit
- /api/v1/me is called frequently leading to before_request_hook getting fired unnecessarily, specifically for sync_user_domain_role leading to more than one syncs happening at once leading to db conflicts/errors
Does the db conflicts/errors happened because of API calls that are simultaneous? Wondering if the same thing can occur if a user is working with multiple tabs at once ?
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.
Thanks for the changes! Looks good to me!
Hey @ajeety4
Spot on. That fix is where I noticed it first. However there can be some weird edge cases where two requests come exactly at the same moment which I am letting go for now since sync will get reattempted at the next request if needed for such edge cases. |
Thanks for the reviews @Charl1996 |
https://dimagi.atlassian.net/browse/SC-3475
Building up on work done in cs/SC-3473-user-roles-from-hq to expire user permissions in an hour to keep permissions up to date with HQ.
Review by commit 🐟
There is quite some refactor, done in separate commits for clarity
All tests ran locally successfully. They wont run here since base branch isn't master.
https://dimagi.atlassian.net/browse/QA-7205