Skip to content

GitHub org/team names should be treated case insensitively (fixes #1167) #1171

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
Mar 7, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions migrations/2017-11-12-195324_lower_teams_names/down.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ALTER TABLE teams
DROP CONSTRAINT teams_login_lowercase_ck;
4 changes: 4 additions & 0 deletions migrations/2017-11-12-195324_lower_teams_names/up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
UPDATE teams SET login=lower(login);
ALTER TABLE teams
ADD CONSTRAINT teams_login_lowercase_ck
CHECK (login = lower(login));
3 changes: 2 additions & 1 deletion src/controllers/krate/owners.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,8 @@ fn modify_owners(req: &mut Request, add: bool) -> CargoResult<Response> {

for login in &logins {
if add {
if owners.iter().any(|owner| owner.login() == *login) {
let login_test = |owner: &Owner| owner.login().to_lowercase() == *login.to_lowercase();
if owners.iter().any(login_test) {
return Err(human(&format_args!("`{}` is already an owner", login)));
}
let msg = krate.owner_add(req.app(), &conn, user, login)?;
Expand Down
13 changes: 10 additions & 3 deletions src/models/team.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,14 @@ impl Team {
format is github:org:team",
)
})?;
Team::create_or_update_github_team(app, conn, login, org, team, req_user)
Team::create_or_update_github_team(
app,
conn,
&login.to_lowercase(),
org,
team,
req_user,
)
}
_ => Err(human(
"unknown organization handler, \
Expand Down Expand Up @@ -140,7 +147,7 @@ impl Team {

let team = teams
.into_iter()
.find(|team| team.slug == team_name)
.find(|team| team.slug.to_lowercase() == team_name.to_lowercase())
.ok_or_else(|| {
human(&format_args!(
"could not find the github team {}/{}",
Expand All @@ -161,7 +168,7 @@ impl Team {
let (handle, resp) = github::github(app, &url, &token)?;
let org: Org = github::parse_github_response(handle, &resp)?;

NewTeam::new(login, team.id, team.name, org.avatar_url)
NewTeam::new(&login.to_lowercase(), team.id, team.name, org.avatar_url)
.create_or_update(conn)
.map_err(Into::into)
}
Expand Down
1 change: 1 addition & 0 deletions src/tests/http-data/team_add_team_mixed_case
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[{"request":{"uri":"http://api.github.com/orgs/Crates-Test-Org/teams?per_page=100","method":"GET","headers":[["Proxy-Connection","Keep-Alive"],["User-Agent","hello!"],["Host","api.github.com"],["Authorization","token 7534f8b996e3a3f800f0a324f619adba12a74532"],["Accept","application/vnd.github.v3+json"]],"body":[]},"response":{"status":200,"headers":[["X-RateLimit-Remaining","4995"],["X-Content-Type-Options","nosniff"],["X-XSS-Protection","1; mode=block"],["X-Runtime-rack","0.035631"],["X-RateLimit-Limit","5000"],["Server","GitHub.com"],["X-Frame-Options","deny"],["X-Accepted-OAuth-Scopes","admin:org, read:org, repo, user, write:org"],["Vary","Accept, Authorization, Cookie, X-GitHub-OTP"],["X-GitHub-Media-Type","github.v3; format=json"],["Content-Length","905"],["ETag","\"168464471f229c2bec917d1c95ad86ff\""],["Access-Control-Allow-Origin","*"],["Content-Type","application/json; charset=utf-8"],["X-GitHub-Request-Id","CA9C:6F2F:8060D6:FB015A:59D4F5CA"],["X-OAuth-Client-Id","89b6afdeaa6c6c7506ec"],["Access-Control-Expose-Headers","ETag, Link, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval"],["X-RateLimit-Reset","1507132377"],["X-OAuth-Scopes","read:org"],["Status","200 OK"],["Date","Wed, 04 Oct 2017 14:52:58 GMT"],["Cache-Control","private, max-age=60, s-maxage=60"],["Strict-Transport-Security","max-age=31536000; includeSubdomains; preload"],["Content-Security-Policy","default-src 'none'"]],"body":[91,123,34,110,97,109,101,34,58,34,99,111,114,101,34,44,34,105,100,34,58,49,54,57,57,51,55,55,44,34,115,108,117,103,34,58,34,99,111,114,101,34,44,34,100,101,115,99,114,105,112,116,105,111,110,34,58,110,117,108,108,44,34,112,114,105,118,97,99,121,34,58,34,115,101,99,114,101,116,34,44,34,117,114,108,34,58,34,104,116,116,112,115,58,47,47,97,112,105,46,103,105,116,104,117,98,46,99,111,109,47,116,101,97,109,115,47,49,54,57,57,51,55,55,34,44,34,109,101,109,98,101,114,115,95,117,114,108,34,58,34,104,116,116,112,115,58,47,47,97,112,105,46,103,105,116,104,117,98,46,99,111,109,47,116,101,97,109,115,47,49,54,57,57,51,55,55,47,109,101,109,98,101,114,115,123,47,109,101,109,98,101,114,125,34,44,34,114,101,112,111,115,105,116,111,114,105,101,115,95,117,114,108,34,58,34,104,116,116,112,115,58,47,47,97,112,105,46,103,105,116,104,117,98,46,99,111,109,47,116,101,97,109,115,47,49,54,57,57,51,55,55,47,114,101,112,111,115,34,44,34,112,101,114,109,105,115,115,105,111,110,34,58,34,97,100,109,105,110,34,125,44,123,34,110,97,109,101,34,58,34,106,117,115,116,45,102,111,114,45,99,114,97,116,101,115,45,50,34,44,34,105,100,34,58,49,54,57,57,51,55,57,44,34,115,108,117,103,34,58,34,106,117,115,116,45,102,111,114,45,99,114,97,116,101,115,45,50,34,44,34,100,101,115,99,114,105,112,116,105,111,110,34,58,34,74,117,115,116,32,102,111,114,32,67,114,97,116,101,115,32,50,34,44,34,112,114,105,118,97,99,121,34,58,34,115,101,99,114,101,116,34,44,34,117,114,108,34,58,34,104,116,116,112,115,58,47,47,97,112,105,46,103,105,116,104,117,98,46,99,111,109,47,116,101,97,109,115,47,49,54,57,57,51,55,57,34,44,34,109,101,109,98,101,114,115,95,117,114,108,34,58,34,104,116,116,112,115,58,47,47,97,112,105,46,103,105,116,104,117,98,46,99,111,109,47,116,101,97,109,115,47,49,54,57,57,51,55,57,47,109,101,109,98,101,114,115,123,47,109,101,109,98,101,114,125,34,44,34,114,101,112,111,115,105,116,111,114,105,101,115,95,117,114,108,34,58,34,104,116,116,112,115,58,47,47,97,112,105,46,103,105,116,104,117,98,46,99,111,109,47,116,101,97,109,115,47,49,54,57,57,51,55,57,47,114,101,112,111,115,34,44,34,112,101,114,109,105,115,115,105,111,110,34,58,34,112,117,108,108,34,125,44,123,34,110,97,109,101,34,58,34,106,117,115,116,45,102,111,114,45,99,114,97,116,101,115,49,34,44,34,105,100,34,58,49,54,57,57,51,55,56,44,34,115,108,117,103,34,58,34,106,117,115,116,45,102,111,114,45,99,114,97,116,101,115,49,34,44,34,100,101,115,99,114,105,112,116,105,111,110,34,58,34,34,44,34,112,114,105,118,97,99,121,34,58,34,115,101,99,114,101,116,34,44,34,117,114,108,34,58,34,104,116,116,112,115,58,47,47,97,112,105,46,103,105,116,104,117,98,46,99,111,109,47,116,101,97,109,115,47,49,54,57,57,51,55,56,34,44,34,109,101,109,98,101,114,115,95,117,114,108,34,58,34,104,116,116,112,115,58,47,47,97,112,105,46,103,105,116,104,117,98,46,99,111,109,47,116,101,97,109,115,47,49,54,57,57,51,55,56,47,109,101,109,98,101,114,115,123,47,109,101,109,98,101,114,125,34,44,34,114,101,112,111,115,105,116,111,114,105,101,115,95,117,114,108,34,58,34,104,116,116,112,115,58,47,47,97,112,105,46,103,105,116,104,117,98,46,99,111,109,47,116,101,97,109,115,47,49,54,57,57,51,55,56,47,114,101,112,111,115,34,44,34,112,101,114,109,105,115,115,105,111,110,34,58,34,112,117,108,108,34,125,93]}},{"request":{"uri":"http://api.github.com/teams/1699377/memberships/crates-tester-2","method":"GET","headers":[["Host","api.github.com"],["User-Agent","hello!"],["Accept","application/vnd.github.v3+json"],["Authorization","token 7534f8b996e3a3f800f0a324f619adba12a74532"],["Proxy-Connection","Keep-Alive"]],"body":[]},"response":{"status":200,"headers":[["X-XSS-Protection","1; mode=block"],["X-RateLimit-Limit","5000"],["Access-Control-Expose-Headers","ETag, Link, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval"],["X-Runtime-rack","0.030002"],["X-OAuth-Scopes","read:org"],["X-GitHub-Request-Id","CA9C:6F2F:8060E2:FB016C:59D4F5CA"],["Access-Control-Allow-Origin","*"],["X-Content-Type-Options","nosniff"],["X-RateLimit-Reset","1507132377"],["X-RateLimit-Remaining","4994"],["Content-Length","111"],["Server","GitHub.com"],["X-Accepted-OAuth-Scopes","admin:org, read:org, repo, write:org"],["X-GitHub-Media-Type","github.v3; format=json"],["Content-Type","application/json; charset=utf-8"],["X-OAuth-Client-Id","89b6afdeaa6c6c7506ec"],["Content-Security-Policy","default-src 'none'"],["Status","200 OK"],["Vary","Accept, Authorization, Cookie, X-GitHub-OTP"],["ETag","\"a004da562b1c421613f0ca38f7a9bf2f\""],["Date","Wed, 04 Oct 2017 14:52:58 GMT"],["X-Frame-Options","deny"],["Strict-Transport-Security","max-age=31536000; includeSubdomains; preload"],["Cache-Control","private, max-age=60, s-maxage=60"]],"body":[123,34,115,116,97,116,101,34,58,34,97,99,116,105,118,101,34,44,34,114,111,108,101,34,58,34,109,97,105,110,116,97,105,110,101,114,34,44,34,117,114,108,34,58,34,104,116,116,112,115,58,47,47,97,112,105,46,103,105,116,104,117,98,46,99,111,109,47,116,101,97,109,115,47,49,54,57,57,51,55,55,47,109,101,109,98,101,114,115,104,105,112,115,47,99,114,97,116,101,115,45,116,101,115,116,101,114,45,50,34,125]}},{"request":{"uri":"http://api.github.com/orgs/Crates-Test-Org","method":"GET","headers":[["Host","api.github.com"],["Proxy-Connection","Keep-Alive"],["User-Agent","hello!"],["Authorization","token 7534f8b996e3a3f800f0a324f619adba12a74532"],["Accept","application/vnd.github.v3+json"]],"body":[]},"response":{"status":200,"headers":[["Server","GitHub.com"],["Status","200 OK"],["X-RateLimit-Remaining","4993"],["X-Runtime-rack","0.052093"],["Cache-Control","private, max-age=60, s-maxage=60"],["Access-Control-Allow-Origin","*"],["Access-Control-Expose-Headers","ETag, Link, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval"],["Content-Length","1168"],["X-OAuth-Scopes","read:org"],["X-OAuth-Client-Id","89b6afdeaa6c6c7506ec"],["X-Accepted-OAuth-Scopes","admin:org, read:org, repo, user, write:org"],["Content-Security-Policy","default-src 'none'"],["X-RateLimit-Limit","5000"],["X-GitHub-Request-Id","CA9C:6F2F:8060E5:FB0178:59D4F5CA"],["X-Frame-Options","deny"],["X-XSS-Protection","1; mode=block"],["Last-Modified","Tue, 18 Aug 2015 17:37:08 GMT"],["Content-Type","application/json; charset=utf-8"],["Strict-Transport-Security","max-age=31536000; includeSubdomains; preload"],["X-RateLimit-Reset","1507132377"],["X-Content-Type-Options","nosniff"],["Vary","Accept, Authorization, Cookie, X-GitHub-OTP"],["ETag","\"164b3fa13f1e681dc06cab6811749c2f\""],["Date","Wed, 04 Oct 2017 14:52:58 GMT"],["X-GitHub-Media-Type","github.v3; format=json"]],"body":[123,34,108,111,103,105,110,34,58,34,99,114,97,116,101,115,45,116,101,115,116,45,111,114,103,34,44,34,105,100,34,58,49,51,56,48,52,50,50,50,44,34,117,114,108,34,58,34,104,116,116,112,115,58,47,47,97,112,105,46,103,105,116,104,117,98,46,99,111,109,47,111,114,103,115,47,99,114,97,116,101,115,45,116,101,115,116,45,111,114,103,34,44,34,114,101,112,111,115,95,117,114,108,34,58,34,104,116,116,112,115,58,47,47,97,112,105,46,103,105,116,104,117,98,46,99,111,109,47,111,114,103,115,47,99,114,97,116,101,115,45,116,101,115,116,45,111,114,103,47,114,101,112,111,115,34,44,34,101,118,101,110,116,115,95,117,114,108,34,58,34,104,116,116,112,115,58,47,47,97,112,105,46,103,105,116,104,117,98,46,99,111,109,47,111,114,103,115,47,99,114,97,116,101,115,45,116,101,115,116,45,111,114,103,47,101,118,101,110,116,115,34,44,34,104,111,111,107,115,95,117,114,108,34,58,34,104,116,116,112,115,58,47,47,97,112,105,46,103,105,116,104,117,98,46,99,111,109,47,111,114,103,115,47,99,114,97,116,101,115,45,116,101,115,116,45,111,114,103,47,104,111,111,107,115,34,44,34,105,115,115,117,101,115,95,117,114,108,34,58,34,104,116,116,112,115,58,47,47,97,112,105,46,103,105,116,104,117,98,46,99,111,109,47,111,114,103,115,47,99,114,97,116,101,115,45,116,101,115,116,45,111,114,103,47,105,115,115,117,101,115,34,44,34,109,101,109,98,101,114,115,95,117,114,108,34,58,34,104,116,116,112,115,58,47,47,97,112,105,46,103,105,116,104,117,98,46,99,111,109,47,111,114,103,115,47,99,114,97,116,101,115,45,116,101,115,116,45,111,114,103,47,109,101,109,98,101,114,115,123,47,109,101,109,98,101,114,125,34,44,34,112,117,98,108,105,99,95,109,101,109,98,101,114,115,95,117,114,108,34,58,34,104,116,116,112,115,58,47,47,97,112,105,46,103,105,116,104,117,98,46,99,111,109,47,111,114,103,115,47,99,114,97,116,101,115,45,116,101,115,116,45,111,114,103,47,112,117,98,108,105,99,95,109,101,109,98,101,114,115,123,47,109,101,109,98,101,114,125,34,44,34,97,118,97,116,97,114,95,117,114,108,34,58,34,104,116,116,112,115,58,47,47,97,118,97,116,97,114,115,50,46,103,105,116,104,117,98,117,115,101,114,99,111,110,116,101,110,116,46,99,111,109,47,117,47,49,51,56,48,52,50,50,50,63,118,61,52,34,44,34,100,101,115,99,114,105,112,116,105,111,110,34,58,110,117,108,108,44,34,104,97,115,95,111,114,103,97,110,105,122,97,116,105,111,110,95,112,114,111,106,101,99,116,115,34,58,116,114,117,101,44,34,104,97,115,95,114,101,112,111,115,105,116,111,114,121,95,112,114,111,106,101,99,116,115,34,58,116,114,117,101,44,34,112,117,98,108,105,99,95,114,101,112,111,115,34,58,48,44,34,112,117,98,108,105,99,95,103,105,115,116,115,34,58,48,44,34,102,111,108,108,111,119,101,114,115,34,58,48,44,34,102,111,108,108,111,119,105,110,103,34,58,48,44,34,104,116,109,108,95,117,114,108,34,58,34,104,116,116,112,115,58,47,47,103,105,116,104,117,98,46,99,111,109,47,99,114,97,116,101,115,45,116,101,115,116,45,111,114,103,34,44,34,99,114,101,97,116,101,100,95,97,116,34,58,34,50,48,49,53,45,48,56,45,49,53,84,48,48,58,48,55,58,51,48,90,34,44,34,117,112,100,97,116,101,100,95,97,116,34,58,34,50,48,49,53,45,48,56,45,49,56,84,49,55,58,51,55,58,48,56,90,34,44,34,116,121,112,101,34,58,34,79,114,103,97,110,105,122,97,116,105,111,110,34,44,34,116,111,116,97,108,95,112,114,105,118,97,116,101,95,114,101,112,111,115,34,58,48,44,34,111,119,110,101,100,95,112,114,105,118,97,116,101,95,114,101,112,111,115,34,58,48,44,34,112,114,105,118,97,116,101,95,103,105,115,116,115,34,58,110,117,108,108,44,34,100,105,115,107,95,117,115,97,103,101,34,58,110,117,108,108,44,34,99,111,108,108,97,98,111,114,97,116,111,114,115,34,58,110,117,108,108,44,34,98,105,108,108,105,110,103,95,101,109,97,105,108,34,58,110,117,108,108,44,34,112,108,97,110,34,58,123,34,110,97,109,101,34,58,34,102,114,101,101,34,44,34,115,112,97,99,101,34,58,57,55,54,53,54,50,52,57,57,44,34,112,114,105,118,97,116,101,95,114,101,112,111,115,34,58,48,44,34,102,105,108,108,101,100,95,115,101,97,116,115,34,58,50,44,34,115,101,97,116,115,34,58,48,125,44,34,100,101,102,97,117,108,116,95,114,101,112,111,115,105,116,111,114,121,95,112,101,114,109,105,115,115,105,111,110,34,58,110,117,108,108,44,34,109,101,109,98,101,114,115,95,99,97,110,95,99,114,101,97,116,101,95,114,101,112,111,115,105,116,111,114,105,101,115,34,58,110,117,108,108,125]}}]
43 changes: 43 additions & 0 deletions src/tests/team.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,49 @@ fn nonexistent_team() {
);
}

// Test adding team names with mixed case
#[test]
fn add_team_mixed_case() {
let (_b, app, middle) = ::app();
let mut req =
::request_with_user_and_mock_crate(&app, &mock_user_on_x_and_y(), "foo_mixed_case");

let body = r#"{"users":["github:Crates-Test-Org:Core"]}"#;

ok_resp!(
middle.call(
req.with_path("/api/v1/crates/foo_mixed_case/owners")
.with_method(Method::Put)
.with_body(body.as_bytes()),
)
);

{
let conn = app.diesel_database.get().unwrap();
let krate = Crate::by_name("foo_mixed_case")
.first::<Crate>(&*conn)
.unwrap();
assert_eq!(krate.owners(&*conn).unwrap().len(), 2);
}

ok_resp!(
middle.call(
req.with_path("/api/v1/crates/foo_mixed_case/owners")
.with_method(Method::Get)
.with_body(body.as_bytes()),
)
);

{
let conn = app.diesel_database.get().unwrap();
let krate = Crate::by_name("foo_mixed_case")
.first::<Crate>(&*conn)
.unwrap();
let owner = &krate.owners(&*conn).unwrap()[1];
assert_eq!(owner.login(), owner.login().to_lowercase());
}
}

// Test adding team as owner when on it
#[test]
fn add_team_as_member() {
Expand Down