Skip to content

Commit b910144

Browse files
Merge #1109
1109: Gh login cant be unique r=sgrif So I can't deploy to production right now because of the unique index added to users.gh_login. The values on production for gh_login aren't unique, and here's why: - Someone can sign up for github with username whatever. - They can sign in to crates.io. We now get their gh_id, say, 3 (which *is* unique) and their gh_login, whatever. Their user account gets crates.io id 10. - They rename or delete their github account, which leaves the username 'whatever' available again. (If they renamed their github account, their gh_id stays stable and we'd migrate their account to their new username next time they log in) - Someone else signs up for github with username wHaTeVeR. - They can sign in to crates.io. We now get their gh_id, 9000, and their gh_login, wHaTeVeR. Their user account gets crates.io id 10000. We can't get rid of the first whatever account, because they might sign in again with a renamed account but the same id, and they should be able to still access the crates they own and all that. All we know is that the account with the latest crates.io id for that lower(gh_login) is the last valid account we know of for that username (and should match the account that you click through to github, as long as they haven't renamed or deleted their github account since). r? @sgrif
2 parents fba2ade + e691e75 commit b910144

File tree

8 files changed

+54
-11
lines changed

8 files changed

+54
-11
lines changed
Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1 @@
1-
DROP INDEX lower_gh_login;
2-
ALTER TABLE users DROP CONSTRAINT unique_gh_login;
3-
CREATE INDEX index_users_gh_login ON users (gh_login);
1+
-- This migration intentionally left blank; see corresponding up.sql
Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1-
DROP INDEX IF EXISTS index_users_gh_login;
2-
ALTER TABLE users ADD CONSTRAINT unique_gh_login UNIQUE(gh_login);
3-
CREATE UNIQUE INDEX lower_gh_login ON users (lower(gh_login));
1+
-- This migration was merged into the master branch but could not be deployed to production
2+
-- because production gh_login isn't actually unique. Later, this migration was commented out
3+
-- so that it will be a no-op on production, and a new migration was added to correct the database
4+
-- of anyone who ran this migration locally.
5+
--
6+
-- DROP INDEX IF EXISTS index_users_gh_login;
7+
-- ALTER TABLE users ADD CONSTRAINT unique_gh_login UNIQUE(gh_login);
8+
-- CREATE UNIQUE INDEX lower_gh_login ON users (lower(gh_login));
9+
SELECT 1;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
-- This file intentionally left blank; see the corresponding up.sql
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
-- This migration should have no effect on production.
2+
-- Its sole purpose is to fix the local database of anyone who ran 2017-09-26-200549 locally--
3+
-- that migration can't be run on production because lower(gh_login) isn't unique on production.
4+
5+
CREATE INDEX IF NOT EXISTS index_users_gh_login ON users (gh_login);
6+
ALTER TABLE users DROP CONSTRAINT IF EXISTS unique_gh_login;
7+
DROP INDEX IF EXISTS lower_gh_login;
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
DROP INDEX IF EXISTS lower_gh_login;
2+
CREATE INDEX index_users_gh_login ON users (gh_login);
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
DROP INDEX IF EXISTS index_users_gh_login;
2+
CREATE INDEX lower_gh_login ON users (lower(gh_login));

src/tests/user.rs

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -81,20 +81,46 @@ fn show() {
8181
}
8282

8383
#[test]
84-
fn show_case_insensitive() {
84+
fn show_latest_user_case_insensitively() {
8585
let (_b, app, middle) = ::app();
8686
{
8787
let conn = t!(app.diesel_database.get());
8888

89+
// Please do not delete or modify the setup of this test in order to get it to pass.
90+
// This setup mimics how GitHub works. If someone abandons a GitHub account, the username is
91+
// available for anyone to take. We need to support having multiple user accounts
92+
// with the same gh_login in crates.io. `gh_id` is stable across renames, so that field
93+
// should be used for uniquely identifying GitHub accounts whenever possible. For the
94+
// crates.io/user/:username pages, the best we can do is show the last crates.io account
95+
// created with that username.
8996
t!(
90-
NewUser::new(1, "foobar", Some("[email protected]"), None, None, "bar")
91-
.create_or_update(&conn)
97+
NewUser::new(
98+
1,
99+
"foobar",
100+
101+
Some("I was first then deleted my github account"),
102+
None,
103+
"bar"
104+
).create_or_update(&conn)
105+
);
106+
t!(
107+
NewUser::new(
108+
2,
109+
"FOOBAR",
110+
111+
Some("I was second, I took the foobar username on github"),
112+
None,
113+
"bar"
114+
).create_or_update(&conn)
92115
);
93116
}
94117
let mut req = ::req(app.clone(), Method::Get, "api/v1/users/fOObAr");
95118
let mut response = ok_resp!(middle.call(&mut req));
96119
let json: UserShowPublicResponse = ::json(&mut response);
97-
assert_eq!("foobar", json.user.login);
120+
assert_eq!(
121+
"I was second, I took the foobar username on github",
122+
json.user.name.unwrap()
123+
);
98124
}
99125

100126
#[test]

src/user/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -456,12 +456,13 @@ pub fn me(req: &mut Request) -> CargoResult<Response> {
456456

457457
/// Handles the `GET /users/:user_id` route.
458458
pub fn show(req: &mut Request) -> CargoResult<Response> {
459-
use self::users::dsl::{gh_login, users};
459+
use self::users::dsl::{gh_login, id, users};
460460

461461
let name = &req.params()["user_id"].to_lowercase();
462462
let conn = req.db_conn()?;
463463
let user = users
464464
.filter(::lower(gh_login).eq(name))
465+
.order(id.desc())
465466
.first::<User>(&*conn)?;
466467

467468
#[derive(Serialize)]

0 commit comments

Comments
 (0)