Skip to content

Cargo owner username disambiguation - #14213

Draft
moskirathe wants to merge 10 commits into
rust-lang:mainfrom
moskirathe:cargo-owner-username-disambiguation
Draft

Cargo owner username disambiguation#14213
moskirathe wants to merge 10 commits into
rust-lang:mainfrom
moskirathe:cargo-owner-username-disambiguation

Conversation

@moskirathe

Copy link
Copy Markdown
Contributor

fixes: #13769

Comment thread crates/crates_io_database/src/models/oauth_github.rs Outdated
Comment thread crates/crates_io_database/src/models/owner.rs
@rustbot

This comment has been minimized.

Comment thread src/controllers/krate/owners.rs Outdated
Comment thread src/controllers/krate/owners.rs Outdated
Comment thread src/controllers/krate/owners.rs
Comment thread src/controllers/krate/owners.rs Outdated
Comment thread crates/crates_io_database/src/models/user.rs Outdated
Comment thread src/controllers/krate/owners.rs Outdated
Comment thread crates/crates_io_database/src/models/user.rs Outdated
Comment thread src/controllers/krate/owners.rs Outdated
Comment thread src/bin/crates-admin/delete_crate.rs Outdated
Comment thread src/controllers/krate/owners.rs Outdated
@moskirathe
moskirathe force-pushed the cargo-owner-username-disambiguation branch from 454d638 to a420795 Compare July 20, 2026 17:57
@rustbot

This comment has been minimized.

@moskirathe
moskirathe force-pushed the cargo-owner-username-disambiguation branch 2 times, most recently from f43ce65 to 64efb97 Compare July 20, 2026 18:13
@moskirathe
moskirathe force-pushed the cargo-owner-username-disambiguation branch 2 times, most recently from 542309c to 6811381 Compare July 20, 2026 18:33
@moskirathe

Copy link
Copy Markdown
Contributor Author

@Turbo87 thanks for the review! I've addressed your comments. the pr is ready for re-review.

@moskirathe
moskirathe requested a review from Turbo87 July 21, 2026 05:21
@moskirathe
moskirathe force-pushed the cargo-owner-username-disambiguation branch 2 times, most recently from a228f14 to 8a44eae Compare July 21, 2026 06:00
@rustbot

This comment has been minimized.

Comment thread crates/crates_io_database/src/models/user.rs
@moskirathe

Copy link
Copy Markdown
Contributor Author

@carols10cents would also love your review on this when you have some time

@moskirathe
moskirathe force-pushed the cargo-owner-username-disambiguation branch from 185aa74 to 1f3ff8a Compare July 25, 2026 14:40
@rustbot

This comment has been minimized.

@moskirathe
moskirathe force-pushed the cargo-owner-username-disambiguation branch from 1f3ff8a to 4c528c3 Compare July 25, 2026 14:44

@carols10cents carols10cents left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Took a quick look and saw one little thing, and I agree with the comments @Turbo87 made.

View changes since this review

Comment thread src/controllers/krate/owners.rs
@rustbot

This comment has been minimized.

@moskirathe
moskirathe force-pushed the cargo-owner-username-disambiguation branch from 3e8fc63 to b09e6a3 Compare August 4, 2026 22:13
@rustbot

This comment has been minimized.

@moskirathe

Copy link
Copy Markdown
Contributor Author

@Turbo87 @carols10cents ready for re-review. thanks!

Comment thread migrations/2026-07-18-120000-0000_add_users_username_index/up.sql Outdated
@moskirathe

Copy link
Copy Markdown
Contributor Author

@Turbo87 @carols10cents still looking for a review on this one when you have some time :)

@rustbot

This comment has been minimized.

@Turbo87
Turbo87 force-pushed the cargo-owner-username-disambiguation branch from ca80a41 to c73265e Compare August 29, 2026 10:49
@rustbot

This comment has been minimized.

@rustbot

This comment has been minimized.

@Turbo87
Turbo87 force-pushed the cargo-owner-username-disambiguation branch from c73265e to 270d425 Compare August 31, 2026 12:31
@rustbot

rustbot commented Aug 31, 2026

Copy link
Copy Markdown
Collaborator

This PR was rebased onto a different main commit. Here's a range-diff highlighting what actually changed.

Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers.

@carols10cents carols10cents left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I haven't quite been through the whole thing but I have to run - there are a few things that need to change and I will look at the rest sometime in the next 24 hours :)

View changes since this review

AND crate_owners.owner_id = crate_owners_with_login.owner_id
AND crate_owners.owner_kind = crate_owners_with_login.owner_kind
AND lower(crate_owners_with_login.login) = lower($2);"#,
AND canon_username(crate_owners_with_login.login) = canon_username($2);"#,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I just noticed that the change to this query, because crate_owners_with_login is selecting both teams.login and users.username as crate_owners_with_login.login, makes it so that we're comparing team names using the canon_username function (which also normalizes hyphen to underscore), whereas before we were only comparing team names using lower.

I don't think we want to change anything about how we handle team names right now. That's under GitHub's control (and we don't have plans to change the way teams work yet).

Looking at the production database, I don't see any team names that differ only by hyphen/underscore today, but I do see some team names with hyphens and some with underscores, so GitHub does allow it. While I don't think it would be a good idea for anyone to do this, I just confirmed that it's possible to create two teams in GitHub, one named crates-io and one named crates_io. I think the change to this query would mean you couldn't manage them independently. If they both existed in crates.io, then you ran cargo owner --add github:rust-lang:crates-io, both the crates-io and the crates_io team would get added, if my reasoning is correct.

Given that we now have this query in owner_remove_with_username and a slightly different query in owner_remove_with_gh_login, maybe we should split owner_remove_with_username into owner_remove_with_username and owner_remove_with_team_name?

) -> QueryResult<OauthGithub> {
oauth_github::table
.filter(canon_username(oauth_github::login).eq(canon_username(login)))
.filter(oauth_github::account_id.ne(-1))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The .filter(oauth_github::account_id.ne(-1)) isn't necessary-- users that have gh_id set to -1 in the users table just don't have any records in the oauth_github table. The -1 values were always sort of a hack and I'm looking forward to getting rid of it with this transition, not continue propagating it :)

gh_login: &str,
gh_id: i32,
) -> QueryResult<i32> {
let user_id = NewUser::builder()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Are you able to use UserBuilder here and...

.insert(conn)
.await?;

NewOauthGithub::builder()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

... OauthGithubBuilder::for_user here?

These test builders might not have existed when you started this PR, but they should make other changes smaller, like the User.gh_login change you mentioned we should do in another comment :)

@@ -0,0 +1 @@
CREATE INDEX CONCURRENTLY IF NOT EXISTS index_oauth_github_login ON oauth_github (lower(login));

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Based on a resolved comment thread on this PR that I'm having trouble linking to and the current state of the query in owner_remove_with_gh_login that has AND canon_username(crate_owners_with_gh_login.login) = canon_username($2), I expected this index to be ON oauth_github (canon_username(login)) rather than lower? Maybe the change got lost in a rebase?

@Turbo87 Turbo87 Sep 1, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

sorry, I wasn't done with refactorings in this branch yet 🙈

I think we should actually do the opposite and keep GitHub account comparisons as only lower(), since hyphen and underscore are not equivalent on the GitHub side, even if we plan on treating them that way for the crates.io usernames.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

oh whoops! sorry-- i'll hold off with the rest of my review til you say you're done :)

sounds good with the github normalization!

@@ -3293,9 +3293,14 @@ export interface operations {
*
* For users, use just the username (e.g., `"octocat"`).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Tiny nit that I think will make this documentation clearer: The note this PR adds about disambiguation of usernames should be added up here with users rather than after the note about teams, and the "just" in "use just the username" should be removed because that's not necessarily true now.

@@ -3358,9 +3363,14 @@ export interface operations {
*
* For users, use just the username (e.g., `"octocat"`).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Same here with moving the disambiguation note up here and removing "just"

Comment thread src/controllers/krate/owners.rs Outdated
@@ -192,7 +195,10 @@ pub struct ChangeOwnersRequest {
///
/// For users, use just the username (e.g., `"octocat"`).

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Same here with moving the disambiguation note up here and removing "just"

Comment thread src/controllers/krate/owners.rs Outdated
let login_test = |owner: &Owner| -> bool {
match parsed_login {
Login::GitHubTeam(_) => {
canon_username(owner.username()) == canon_username(login)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Similarly to my comment on owner_remove_with_username, I think this is changing the way we handle team names in a way we don't want to change in this PR. Team names should still only use lower.

.select(User::as_select())
.first(conn)
.await
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Note for myself: this is where i stopped reviewing and saved the review

The helper mirrors the PostgreSQL function so in-memory owner matching uses
the same case folding and `-` to `_` normalization as database queries.
The add-owner endpoint accepts `crates.io:username` arguments and resolves them using crates.io username canonicalization. Owner invitations now retain the selected username so responses identify the requested crates.io account without changing legacy unprefixed behavior.
The expression index supports queries using `lower(oauth_github.login)`.
Concurrent creation and removal avoid blocking writes and therefore run
outside a transaction.
`OauthGithub::find_by_login()` selects the matching account with the highest
account ID when historical rows differ only by case. Joined `User` queries
also expose the OAuth login required by owner resolution.
The add-owner endpoint accepts `github:username` arguments and resolves them through linked OAuth accounts. Shared owner-argument validation distinguishes GitHub users from teams while both prefixed removal forms remain unsupported.
`Crate` provides separate removal queries for canonical crates.io usernames
and linked GitHub logins. Owner routes select the query for the parsed user or
team argument.
The remove-owner endpoint accepts `crates.io:username` arguments and removes the current owner resolved through crates.io username canonicalization. GitHub-prefixed removal arguments remain rejected until their corresponding removal support is enabled.
The remove-owner endpoint accepts `github:username` arguments and removes the current owner resolved through the GitHub login namespace. The owner request documentation now describes both service prefixes for additions and removals.
Unprefixed additions resolve canonical crates.io usernames and require the
linked GitHub login to identify the same name. Ambiguous accounts receive
explicit `crates.io:` and `github:` commands for selecting the intended user.
Unprefixed removals compare the crate's current owners by canonical crates.io
username and linked GitHub login. A sole match is removed, while distinct
owners with the same supplied name produce an ambiguity error.
@rustbot

rustbot commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-backend ⚙️ A-frontend 🐹 C-enhancement ✨ Category: Adding new behavior or a change to the way an existing feature works

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement username disambiguation in cargo owner param handling and error messages

4 participants