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
1696 update rest api for access control #1704
Conversation
@aphelionz This PR needs some update to resolve conflicts as well failing test. |
@pkdash Thanks for the nudge :) All set. |
|
||
class ResourceAccessUpdateDelete(generics.RetrieveUpdateDestroyAPIView): | ||
""" | ||
Read, update, or delete a resource |
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.
Probably this comment be "Read, update or delete access permission for a resource"
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.
Fixed
:return: (on success): Success or Error JSON object | ||
|
||
:type str | ||
:param user_id: user ID to remove |
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.
Parameter description seems not correct.
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.
Fixed
message = "Request must contain a 'resource' ID as well as a 'user_id' or 'group_id'" | ||
return Response( | ||
data={'error': message}, | ||
status=status.HTTP_200_OK |
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 the status code be HTTP_400_BAD_REQUEST
?
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.
Fixed
message = "Request must contain a 'resource' ID as well as a 'user_id' or 'group_id'" | ||
return Response( | ||
data={'error': message}, | ||
status=status.HTTP_200_OK |
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.
status code BAD_REQUEST?
def get_queryset(self, pk, user): | ||
resource = hydroshare.get_resource_by_shortkey(shortkey=pk) | ||
|
||
if resource.user_id == user.id: |
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.
Not sure how this if
statement works. I don't think resource object has attribute/property user_id
. user
can be an anonymous user.
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 works. It allows resource owners to see all resource access but non-owners to only see their own access.
That being said i'm perfectly amenable to a different way of doing this
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 is nothing wrong about the logic. I was just wondering how you are able to access the user_id
of the resource object. Does the resource object has such an attribute?
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.
Indeed it does! @pkdash
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.
@aphelionz I assume we are able to access an instance of user
from an instance of resource
due to the inheritance of Resource class from Ownable (mezzanine code) class.
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.
@aphelionz It seems the user_id
is the id of the user who originally created the resource. So we can't use user_id
to check if the requesting user is the owner of the resource. I think the following check should work:
if user in resource.raccess.owners:
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.
Fixed.
elif needed_permission == ACTION_TO_AUTHORIZE.VIEW_RESOURCE_ACCESS: | ||
authorized = user.uaccess.can_view_resource(res) | ||
elif needed_permission == ACTION_TO_AUTHORIZE.EDIT_RESOURCE_ACCESS: | ||
authorized = user.uaccess.owns_resource(res) |
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 authorized = user.uaccess.can_edit_resource(res)
?
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 that's how the UI works, but we should definitely clarify if we want any editor to edit resource access, or only the owner.
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.
@alvacouch can you clarify this?
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.
@alvacouch ping
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.
@aphelionz @pkdash This does not conform to the engine. According to the engine, anyone can share something with someone else, but only at the privilege that person holds. So, being able to edit permissions requires VIEW access..... but that isn't enough..... what do you mean by "edit"?
user.uaccess.can_share_resource
is appropriate.
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.
Fixed.
|
||
def tearDown(self): | ||
super(TestSetAccessRules, self).tearDown() | ||
self.secondUser.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.
Do we need these deletes?
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 think yes, based on the tearDown method in /hs_core/tests/api/rest/base.py. Am I wrong?
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 am not sure if the implementation of teardown() in base.py is correct. Shouldn't it be calling the super()?. I assume the APITestCase is an extension of the Django's TestCase class. When subclassing the TestCase I believe there is no need to override the teardown() for database cleanup as that's the default behavior. I may be wrong.
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 not sure either. Who can answer this? @alvacouch ?
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.
@aphelionz @pkdash I have not looked this over much.... I didn't write this suite of tests. Check hs_access_control/tests for mine.....
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.
@alvacouch In you tests it seems you are also explicitly deleting the database objects using the function global_reset()
. Also, it seems all access control tests pass without the use of global_reset()
.
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.
In general, the tests don't reset, and using --keepdb leads to different results every time. I have made a practice of resetting and cleaning up after myself, which at least insures that --keepdb works for me. If you repeat test --keepdb several times, you will see the number of errors grow as state accumulates, and only the tests for which there is local cleanup work properly in --keepdb. The initial global_reset is redundant, I admit, but it is without cost.
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 are some methods in the super that might be useful on occasion. In this case, they don't seem to be relevant.
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 then am OK with leaving the deletes in trearDown().
self.assertEqual( | ||
"Request cannot contain both a 'user_id' and a 'group_id' parameter.", | ||
put_response.data['error']) | ||
|
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.
Another error case you may consider where a user who does not have sufficient permission on the resource tries to grant/revoke permission.
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 will add negative cases.
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.
Negative cases added :)
else: | ||
querysets = ( | ||
UserResourcePrivilege.objects.filter(resource=resource, user=user), | ||
GroupResourcePrivilege.objects.filter(resource=resource, user=user) |
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.
GroupResourcePrivelege
class doesn't have user
field.
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.
Fixed
|
||
if "user_id" in keys and "privilege" in keys: | ||
user_to_add = utils.user_from_id(request.data['user_id']) | ||
user_access.share_resource_with_user(resource, user_to_add, request.data['privilege']) |
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 also be within try ... except
?
Also should we be validating the value in request.data['privilege']
before using it?
@aphelionz I have left few additional comments. It would be good to close this PR soon. |
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.
@pkdash All current feedback is implemented or otherwise handled. Let me know!
elif needed_permission == ACTION_TO_AUTHORIZE.VIEW_RESOURCE_ACCESS: | ||
authorized = user.uaccess.can_view_resource(res) | ||
elif needed_permission == ACTION_TO_AUTHORIZE.EDIT_RESOURCE_ACCESS: | ||
authorized = user.uaccess.owns_resource(res) |
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.
Fixed.
@aphelionz Looks good. +1 |
Starting the PR process for this so I can keep an eye on Jenkins while I do the next parts. - will update with docs and tests over the next day or so.
This exposes a new endpoint at /hsapi/resource/[id]/access/.
group_id
oruser_id
will remove access.