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

Fixed bug w/ not checking permissions in copy #174

Merged
merged 10 commits into from
Dec 21, 2023

Conversation

kelockhart
Copy link
Member

Bug fix: users were erroneously able to copy their own library into a library they did not have permissions to edit

@@ -207,6 +207,10 @@ def post(self, library):
if data['action'] in ['union', 'intersection', 'difference']:
if 'libraries' not in data:
return err(NO_LIBRARY_SPECIFIED_ERROR)
for lib in data['libraries']:
if not self.read_access(service_uid=user_editing_uid,
Copy link
Member Author

Choose a reason for hiding this comment

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

This should be a check on if the user has read access, or if the library is public

Copy link
Contributor

Choose a reason for hiding this comment

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

Just added the access check when library is public. A slug is passed for each library, so we have to convert it to UUID and then get the library to check if it's public. I noticed there was code right below that sort of did that, so I moved some things around.

return err(NO_LIBRARY_SPECIFIED_ERROR)
for lib in check_access:
lib = session.merge(lib)
if lib.public or not self.read_access(service_uid=user_editing_uid,
Copy link
Member Author

Choose a reason for hiding this comment

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

Whoops my comment wasn't very clear - this should be if not lib.public or not self.read_access...

if 'libraries' not in data:
return err(NO_LIBRARY_SPECIFIED_ERROR)
for lib in check_access:
lib = session.merge(lib)
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 haven't used merge before - lib is the same as secondary, as returned by the query in line 217. Do you need to do the merge into the session, even though this should already be a sqlalchemy object?

Copy link
Contributor

Choose a reason for hiding this comment

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

Totally get it! The reason I used merge was because the libraries were getting detached from the session. I'm sure there's some other way around it, but I couldn't think of any off the top of my head. If you're okay with holding off on this until tomorrow, I can try to come up with something else.

Copy link
Contributor

@tjacovich tjacovich left a comment

Choose a reason for hiding this comment

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

Overall I think we are nearly there. One quick question on a test and a modification of the logic for checking if we have permissions for the secondary libraries.

@@ -718,7 +718,7 @@ def test_operations_view_post_types(self):
self.assertEqual(response.json['error'], NO_LIBRARY_SPECIFIED_ERROR['body'])

post_data['action'] = 'copy'
post_data['libraries'] = ['lib1', 'lib2']
post_data['libraries'] = [library_id_2, library_id_2]
Copy link
Contributor

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 matters for the test, but did you mean for these to both be the same library?

Copy link
Contributor

@femalves femalves Dec 14, 2023

Choose a reason for hiding this comment

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

Yeah, it wasn't relevant so I added lib2. But I changed it now to lib1, just because that's more consistent with how it was before.

library_id=library_uuid):
has_read_access_primary = True

for lib in data.get('libraries', []):
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this might miss a corner case where if someone specifies a mix of libraries with various access types. This isn't an issue for the copy method because we limit the number of secondary libraries to 1, but for other methods, they could potentially acquire access to a secondary library they are not supposed to have (ie. a user could compute the union with three libraries where they only have access to two of them and gain access to the documents stored in the third library.) I have to double-check all the methods but a simple first past fix might be to raise an exception immediately if a user has no permissions on a private secondary library.

Copy link
Contributor

Choose a reason for hiding this comment

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

Thank you for catching that. I had changed the logic a little bit and forgot to account for the implications here. Good one. I'll fix it right away.

Copy link
Contributor

@tjacovich tjacovich left a comment

Choose a reason for hiding this comment

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

I think this PR is basically there. The logic is all sound, I just had one comment on the handling of the primary library in the permissions check.

)

if action in ["union", "intersection", "difference"]:
if not has_read_access_primary or not (
Copy link
Contributor

Choose a reason for hiding this comment

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

Looks good! The only comment I have, and I might be misunderstanding this, is that we can also perform union, intersection, and difference actions with a public primary library. In that case, you might be able to move the logical check for the primary out of the loop and immediately exit if the primary permissions aren't set and then have checks only on the secondary in the loop. Would potentially save some postgres queries.

Copy link
Contributor

Choose a reason for hiding this comment

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

For the union, intersection, and difference we need a public secondary library, so I think it makes sense as it is right now. Feel free to correct me.

Copy link
Contributor

Choose a reason for hiding this comment

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

I think you are entirely correct on that point. I'm wondering if has_read_access_primary should be (primary_is_public or has_read_access_primary) as it is in the check for copy. If that is the case we may be able to keep the checks in the loop to only looking at the secondary because the requirements for the primary are the same.

Copy link
Contributor

Choose a reason for hiding this comment

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

That's the thing. For copy we need (primary_is_public or has_read_access_primary) but for union, intersection, difference we need has_read_access_primary.
At least that is my understanding.

Here's my notes:
copy:

  • primary: has_read_access or is_public
  • secondary: has_write_access

union, intersection, difference:

  • primary: has_read_access
  • secondary: has_read_access or is_public

Does that make sense to you?
I talked to Kelly and checked a couple of times, but we might have missed something.

Copy link
Member Author

@kelockhart kelockhart left a comment

Choose a reason for hiding this comment

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

Looks good! I think this is ready.

Copy link
Contributor

@tjacovich tjacovich left a comment

Choose a reason for hiding this comment

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

I agree. Looks ready to me.


if data['action'] == 'union':

if action == "empty":
Copy link
Contributor

Choose a reason for hiding this comment

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

I really like this change to use permission_check_<>

Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks. It was pretty readable in the end.

@femalves femalves merged commit 34ba5bd into adsabs:master Dec 21, 2023
1 check passed
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.

3 participants