Skip to content

Fix checks and consistency in the API endpoints to manage ownership. #1772

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

Merged
merged 5 commits into from
Jul 16, 2019

Conversation

smarnach
Copy link
Contributor

Fixes #1583.

The API endpoints to add and remove owners for a crate has multiple issues.

  • The database interactions are not wrapped in a transaction, making them prone to race conditions.

  • The lack of a transaction can also result in partially applied operations, e.g. when adding or
    removing multiple owners at once.

  • The check whether the last owner was removed only triggers if the original list of owners had a
    length of exactly one. This is wrong in at least two cases:

    • Teams do not have permission to manage ownership, so they should not be counted.

    • If multiple users are removed at once, we can end up with no owners even when there originally
      was more than one user.

This change fixes all mentioned problems, and adds tests for all bullet points except for the first
one (which can't be tested with reasonable effort).

@smarnach smarnach force-pushed the fix-owner-checks branch 2 times, most recently from ff88830 to 7976674 Compare June 20, 2019 20:05
@bors
Copy link
Contributor

bors commented Jun 26, 2019

☔ The latest upstream changes (presumably #1760) made this pull request unmergeable. Please resolve the merge conflicts.

@sgrif
Copy link
Contributor

sgrif commented Jun 27, 2019

Can we split the transaction wrapping into its own commit? I don't anticipate any problems with this, but in the event we need to roll something back it's helpful to have each problem solved separately

@smarnach
Copy link
Contributor Author

smarnach commented Jun 27, 2019

@sgrif If I split this up, the transaction wrapping will necessarily be the first commit, since everything else depends on it. This means we can't roll back the transaction wrapping while keeping any of the other changes, if this is what you have in mind, but we will still be able to roll back some of the other fixes and keep the transaction wrapping.

@sgrif
Copy link
Contributor

sgrif commented Jun 27, 2019

Yes, having the transaction wrapping first was what I had in mind

@smarnach
Copy link
Contributor Author

@sgrif I've split this up in three commits (to improve diff readability), and made sure the tests pass for each step (to facilitate partial rollbacks). Please take another look.

Copy link
Member

@jtgeibel jtgeibel left a comment

Choose a reason for hiding this comment

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

Thanks @smarnach! This PR looks great overall. I have one small suggestion below, although I'm fine merging as-is if you think it makes more sense to have a separate PR to use the json! macro more consistently in tests. (It looks like there are at least 2 more similar uses of format! in tests/user.rs.)

let body = format!("{{\"users\":[\"{}\"]}}", owner);
self.delete_with_body(&url, body.as_bytes())
let body = format!("{{\"owners\":[\"{}\"]}}", owners.join("\", \""));
method(&self, &url, body.as_bytes())
Copy link
Member

Choose a reason for hiding this comment

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

It's probably worth switching to the json! macro here.

let body = json!({ "owners": owners });
method(&self, &url, body.to_string().as_bytes())

@smarnach
Copy link
Contributor Author

@jtgeibel Thanks for the review! Using the json! macro is certainly a lot more readable, and the changes are very low risk, so I can include them here.

I noticed another race condition I haven't fixed yet, though. When adding new owners, the code first reads the list of all current owners from the DB, then for each owner checks whether it occurs in the list of current owners, then creates a new owner invitation. If there already was an owner invitation for an owner, and it gets accepted right after the code has checked for the list of current owners, we will create a new invitation for a user that is already an owner, which would normally be prohibited.

This race condition could be fixed by using the "serializable" isolation level for the transaction, but then we would also need to add code to retry the transaction if it fails due to a serialization error. I don't think this would be worth it – the above race condition should be exceedingly rare, and even when it gets triggered, the impact is negligible – there will be a spurious invitation, and accepting or rejecting it has no effect, since the invitee already is an owner.

@sgrif
Copy link
Contributor

sgrif commented Jul 16, 2019

@bors: r+

@bors
Copy link
Contributor

bors commented Jul 16, 2019

📌 Commit 794cc87 has been approved by sgrif

@bors
Copy link
Contributor

bors commented Jul 16, 2019

⌛ Testing commit 794cc87 with merge 2a036e5...

bors added a commit that referenced this pull request Jul 16, 2019
Fix checks and consistency in the API endpoints to manage ownership.

Fixes #1583.

The API endpoints to add and remove owners for a crate has multiple issues.

* The database interactions are not wrapped in a transaction, making them prone to race conditions.

* The lack of a transaction can also result in partially applied operations, e.g. when adding or
  removing multiple owners at once.

* The check whether the last owner was removed only triggers if the _original_ list of owners had a
  length of exactly one. This is wrong in at least two cases:

  * Teams do not have permission to manage ownership, so they should not be counted.

  * If multiple users are removed at once, we can end up with no owners even when there originally
    was more than one user.

This change fixes all mentioned problems, and adds tests for all bullet points except for the first
one (which can't be tested with reasonable effort).
@bors
Copy link
Contributor

bors commented Jul 16, 2019

☀️ Test successful - checks-travis
Approved by: sgrif
Pushing 2a036e5 to master...

@bors bors merged commit 794cc87 into rust-lang:master Jul 16, 2019
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.

It's possible to permanently lock yourself out of crate ownership through teams
5 participants