Skip to content

Commit 38e7272

Browse files
authored
Allow removing deleted organizations from crate owners (#7051)
1 parent 379fcaa commit 38e7272

File tree

8 files changed

+65
-27
lines changed

8 files changed

+65
-27
lines changed

Diff for: src/controllers/krate/owners.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ fn modify_owners(
141141
msgs.join(",")
142142
} else {
143143
for login in &logins {
144-
krate.owner_remove(app, conn, user, login)?;
144+
krate.owner_remove(conn, login)?;
145145
}
146146
if User::owning(&krate, conn)?.is_empty() {
147147
return Err(cargo_err(

Diff for: src/models/krate.rs

+2-8
Original file line numberDiff line numberDiff line change
@@ -339,14 +339,8 @@ impl Crate {
339339
}
340340
}
341341

342-
pub fn owner_remove(
343-
&self,
344-
app: &App,
345-
conn: &mut PgConnection,
346-
req_user: &User,
347-
login: &str,
348-
) -> AppResult<()> {
349-
let owner = Owner::find_or_create_by_login(app, conn, req_user, login)?;
342+
pub fn owner_remove(&self, conn: &mut PgConnection, login: &str) -> AppResult<()> {
343+
let owner = Owner::find_by_login(conn, login)?;
350344

351345
let target = crate_owners::table.find((self.id(), owner.id(), owner.kind()));
352346
diesel::update(target)

Diff for: src/models/owner.rs

+26-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
use diesel::pg::Pg;
22
use diesel::prelude::*;
33

4-
use crate::app::App;
54
use crate::util::errors::{cargo_err, AppResult};
5+
use crate::{app::App, schema::teams};
66

77
use crate::models::{Crate, Team, User};
88
use crate::schema::{crate_owners, users};
@@ -59,6 +59,7 @@ impl Owner {
5959
/// up-to-date GitHub ID. Fails out if the user isn't found in the
6060
/// database, the team isn't found on GitHub, or if the user isn't a member
6161
/// of the team on GitHub.
62+
///
6263
/// May be a user's GH login or a full team name. This is case
6364
/// sensitive.
6465
pub fn find_or_create_by_login(
@@ -82,6 +83,30 @@ impl Owner {
8283
}
8384
}
8485

86+
/// Finds the owner by name. Never recreates a team, to ensure that
87+
/// organizations that were deleted after they were added can still be
88+
/// removed.
89+
///
90+
/// May be a user's GH login or a full team name. This is case
91+
/// sensitive.
92+
pub fn find_by_login(conn: &mut PgConnection, name: &str) -> AppResult<Owner> {
93+
if name.contains(':') {
94+
teams::table
95+
.filter(lower(teams::login).eq(&name.to_lowercase()))
96+
.first(conn)
97+
.map(Owner::Team)
98+
.map_err(|_| cargo_err(&format_args!("could not find team with login `{name}`")))
99+
} else {
100+
users::table
101+
.filter(lower(users::gh_login).eq(name.to_lowercase()))
102+
.filter(users::gh_id.ne(-1))
103+
.order(users::gh_id.desc())
104+
.first(conn)
105+
.map(Owner::User)
106+
.map_err(|_| cargo_err(&format_args!("could not find user with login `{name}`")))
107+
}
108+
}
109+
85110
pub fn kind(&self) -> i32 {
86111
match *self {
87112
Owner::User(_) => OwnerKind::User as i32,

Diff for: src/tests/owners.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -566,9 +566,7 @@ fn deleted_ownership_isnt_in_owner_user() {
566566

567567
app.db(|conn| {
568568
let krate = CrateBuilder::new("foo_my_packages", user.id).expect_build(conn);
569-
krate
570-
.owner_remove(app.as_inner(), conn, user, &user.gh_login)
571-
.unwrap();
569+
krate.owner_remove(conn, &user.gh_login).unwrap();
572570
});
573571

574572
let json: UserResponse = anon.get("/api/v1/crates/foo_my_packages/owner_user").good();

Diff for: src/tests/routes/crates/list.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -839,9 +839,7 @@ fn crates_by_user_id_not_including_deleted_owners() {
839839

840840
app.db(|conn| {
841841
let krate = CrateBuilder::new("foo_my_packages", user.id).expect_build(conn);
842-
krate
843-
.owner_remove(app.as_inner(), conn, user, "foo")
844-
.unwrap();
842+
krate.owner_remove(conn, "foo").unwrap();
845843
});
846844

847845
let response = anon.search_by_user_id(user.id);

Diff for: src/tests/routes/me/get.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,7 @@ fn test_user_owned_crates_doesnt_include_deleted_ownership() {
4242

4343
app.db(|conn| {
4444
let krate = CrateBuilder::new("foo_my_packages", user_model.id).expect_build(conn);
45-
krate
46-
.owner_remove(app.as_inner(), conn, user_model, &user_model.gh_login)
47-
.unwrap();
45+
krate.owner_remove(conn, &user_model.gh_login).unwrap();
4846
});
4947

5048
let json = user.show_me();

Diff for: src/tests/routes/users/stats.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ fn user_total_downloads() {
3939
.execute(conn)
4040
.unwrap();
4141
no_longer_my_krate
42-
.owner_remove(app.as_inner(), conn, user, &user.gh_login)
42+
.owner_remove(conn, &user.gh_login)
4343
.unwrap();
4444
});
4545

Diff for: src/tests/team.rs

+32-7
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@ use crate::{
33
builders::{CrateBuilder, PublishBuilder},
44
new_team, OwnerTeamsResponse, RequestHelper, TestApp,
55
};
6-
use crates_io::models::{Crate, NewTeam};
6+
use crates_io::{
7+
models::{Crate, NewTeam},
8+
schema::teams,
9+
};
710

811
use diesel::*;
912
use http::StatusCode;
@@ -67,14 +70,15 @@ fn one_colon() {
6770
}
6871

6972
#[test]
70-
fn nonexistent_team() {
73+
fn add_nonexistent_team() {
7174
let (app, _, user, token) = TestApp::init().with_token();
7275

7376
app.db(|conn| {
74-
CrateBuilder::new("foo_nonexistent", user.as_model().id).expect_build(conn);
77+
CrateBuilder::new("foo_add_nonexistent", user.as_model().id).expect_build(conn);
7578
});
7679

77-
let response = token.add_named_owner("foo_nonexistent", "github:test-org:this-does-not-exist");
80+
let response =
81+
token.add_named_owner("foo_add_nonexistent", "github:test-org:this-does-not-exist");
7882
assert_eq!(response.status(), StatusCode::OK);
7983
assert_eq!(
8084
response.into_json(),
@@ -268,6 +272,29 @@ fn remove_team_as_team_owner() {
268272
);
269273
}
270274

275+
#[test]
276+
fn remove_nonexistent_team() {
277+
let (app, _, user, token) = TestApp::init().with_token();
278+
279+
app.db(|conn| {
280+
CrateBuilder::new("foo_remove_nonexistent", user.as_model().id).expect_build(conn);
281+
insert_into(teams::table)
282+
.values((
283+
teams::login.eq("github:test-org:this-does-not-exist"),
284+
teams::github_id.eq(5678),
285+
))
286+
.execute(conn)
287+
.expect("couldn't insert nonexistent team")
288+
});
289+
290+
token
291+
.remove_named_owner(
292+
"foo_remove_nonexistent",
293+
"github:test-org:this-does-not-exist",
294+
)
295+
.good();
296+
}
297+
271298
/// Test trying to publish a crate we don't own
272299
#[test]
273300
fn publish_not_owned() {
@@ -422,9 +449,7 @@ fn crates_by_team_id_not_including_deleted_owners() {
422449

423450
let krate = CrateBuilder::new("foo", user.id).expect_build(conn);
424451
add_team_to_crate(&t, &krate, user, conn).unwrap();
425-
krate
426-
.owner_remove(app.as_inner(), conn, user, &t.login)
427-
.unwrap();
452+
krate.owner_remove(conn, &t.login).unwrap();
428453
t
429454
});
430455

0 commit comments

Comments
 (0)