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

1696 update rest api for access control #1704

Merged
merged 21 commits into from Jan 12, 2017

Conversation

aphelionz
Copy link
Contributor

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/.

  • A GET request by a resource owner will show all users and groups associated
  • A GET request by a non-owners will only show their access and group access
  • A PUT request by an owner with a 'group_id' or 'user_id', along with a 'privilege' int will add access
  • A DELETE request by an owner with a group_id or user_id will remove access.

@pkdash pkdash self-assigned this Dec 11, 2016
@pkdash
Copy link
Member

pkdash commented Dec 20, 2016

@aphelionz This PR needs some update to resolve conflicts as well failing test.

@aphelionz
Copy link
Contributor Author

@pkdash Thanks for the nudge :) All set.


class ResourceAccessUpdateDelete(generics.RetrieveUpdateDestroyAPIView):
"""
Read, update, or delete a resource
Copy link
Member

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"

Copy link
Contributor Author

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
Copy link
Member

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.

Copy link
Contributor Author

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
Copy link
Member

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?

Copy link
Contributor Author

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
Copy link
Member

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:
Copy link
Member

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.

Copy link
Contributor Author

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

Copy link
Member

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?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Indeed it does! @pkdash

Copy link
Member

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.

Copy link
Member

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:

Copy link
Contributor Author

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)
Copy link
Member

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)?

Copy link
Contributor Author

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.

Copy link
Member

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?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@alvacouch ping

Copy link
Contributor

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.

Copy link
Contributor Author

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()
Copy link
Member

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?

Copy link
Contributor Author

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?

Copy link
Member

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.

Copy link
Contributor Author

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 ?

Copy link
Contributor

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.....

Copy link
Member

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().

Copy link
Contributor

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.

Copy link
Contributor

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.

Copy link
Member

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'])

Copy link
Member

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.

Copy link
Contributor Author

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.

Copy link
Contributor Author

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)
Copy link
Member

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.

Copy link
Contributor Author

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'])
Copy link
Member

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?

@pkdash
Copy link
Member

pkdash commented Jan 10, 2017

@aphelionz I have left few additional comments. It would be good to close this PR soon.

Copy link
Contributor Author

@aphelionz aphelionz left a 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)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

@pkdash
Copy link
Member

pkdash commented Jan 12, 2017

@aphelionz Looks good. +1

@aphelionz aphelionz merged commit 09878d5 into develop Jan 12, 2017
@mjstealey mjstealey deleted the 1696-update-REST-API-for-access-control branch February 16, 2017 16:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants