Skip to content

Commit 100bcd9

Browse files
committed
models/team: Move Team::create_or_update_github_team() fn into controller
This fn is doing way to much non-database-related stuff, so it shouldn't live in the `models` module.
1 parent 8f7c4c5 commit 100bcd9

File tree

3 files changed

+69
-76
lines changed

3 files changed

+69
-76
lines changed

src/controllers/krate/owners.rs

Lines changed: 66 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22
33
use crate::controllers::krate::CratePath;
44
use crate::models::krate::OwnerRemoveError;
5+
use crate::models::team::can_add_team;
56
use crate::models::{
67
krate::NewOwnerInvite, token::EndpointScope, CrateOwner, NewCrateOwnerInvitation,
7-
NewCrateOwnerInvitationOutcome,
8+
NewCrateOwnerInvitationOutcome, NewTeam,
89
};
910
use crate::models::{Crate, Owner, Rights, Team, User};
1011
use crate::util::errors::{bad_request, crate_not_found, custom, AppResult, BoxedAppError};
@@ -21,6 +22,7 @@ use diesel_async::scoped_futures::ScopedFutureExt;
2122
use diesel_async::{AsyncConnection, AsyncPgConnection, RunQueryDsl};
2223
use http::request::Parts;
2324
use http::StatusCode;
25+
use oauth2::AccessToken;
2426
use secrecy::{ExposeSecret, SecretString};
2527
use thiserror::Error;
2628

@@ -353,15 +355,9 @@ async fn add_team_owner(
353355
})?;
354356

355357
// Always recreate teams to get the most up-to-date GitHub ID
356-
let team = Team::create_or_update_github_team(
357-
gh_client,
358-
conn,
359-
&login.to_lowercase(),
360-
org,
361-
team,
362-
req_user,
363-
)
364-
.await?;
358+
let team =
359+
create_or_update_github_team(gh_client, conn, &login.to_lowercase(), org, team, req_user)
360+
.await?;
365361

366362
// Teams are added as owners immediately, since the above call ensures
367363
// the user is a team member.
@@ -376,6 +372,66 @@ async fn add_team_owner(
376372
Ok(NewOwnerInvite::Team(team))
377373
}
378374

375+
/// Tries to create or update a Github Team. Assumes `org` and `team` are
376+
/// correctly parsed out of the full `name`. `name` is passed as a
377+
/// convenience to avoid rebuilding it.
378+
pub async fn create_or_update_github_team(
379+
gh_client: &dyn GitHubClient,
380+
conn: &mut AsyncPgConnection,
381+
login: &str,
382+
org_name: &str,
383+
team_name: &str,
384+
req_user: &User,
385+
) -> AppResult<Team> {
386+
// GET orgs/:org/teams
387+
// check that `team` is the `slug` in results, and grab its data
388+
389+
// "sanitization"
390+
fn is_allowed_char(c: char) -> bool {
391+
matches!(c, 'a'..='z' | 'A'..='Z' | '0'..='9' | '-' | '_')
392+
}
393+
394+
if let Some(c) = org_name.chars().find(|c| !is_allowed_char(*c)) {
395+
return Err(bad_request(format_args!(
396+
"organization cannot contain special \
397+
characters like {c}"
398+
)));
399+
}
400+
401+
let token = AccessToken::new(req_user.gh_access_token.expose_secret().to_string());
402+
let team = gh_client.team_by_name(org_name, team_name, &token).await
403+
.map_err(|_| {
404+
bad_request(format_args!(
405+
"could not find the github team {org_name}/{team_name}. \
406+
Make sure that you have the right permissions in GitHub. \
407+
See https://doc.rust-lang.org/cargo/reference/publishing.html#github-permissions"
408+
))
409+
})?;
410+
411+
let org_id = team.organization.id;
412+
let gh_login = &req_user.gh_login;
413+
414+
if !can_add_team(gh_client, org_id, team.id, gh_login, &token).await? {
415+
return Err(custom(
416+
StatusCode::FORBIDDEN,
417+
"only members of a team or organization owners can add it as an owner",
418+
));
419+
}
420+
421+
let org = gh_client.org_by_name(org_name, &token).await?;
422+
423+
NewTeam::builder()
424+
.login(&login.to_lowercase())
425+
.org_id(org_id)
426+
.github_id(team.id)
427+
.maybe_name(team.name.as_deref())
428+
.maybe_avatar(org.avatar_url.as_deref())
429+
.build()
430+
.create_or_update(conn)
431+
.await
432+
.map_err(Into::into)
433+
}
434+
379435
/// Error results from a [`add_owner()`] model call.
380436
#[derive(Debug, Error)]
381437
enum OwnerAddError {

src/models.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ mod keyword;
3333
pub mod krate;
3434
mod owner;
3535
mod rights;
36-
mod team;
36+
pub mod team;
3737
pub mod token;
3838
pub mod user;
3939
pub mod version;

src/models/team.rs

Lines changed: 2 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,11 @@
11
use bon::Builder;
22
use diesel::prelude::*;
33
use diesel_async::{AsyncPgConnection, RunQueryDsl};
4-
use http::StatusCode;
5-
6-
use crate::util::errors::{bad_request, custom, AppResult};
74

85
use crates_io_github::{GitHubClient, GitHubError};
96
use oauth2::AccessToken;
10-
use secrecy::ExposeSecret;
117

12-
use crate::models::{Crate, CrateOwner, Owner, OwnerKind, User};
8+
use crate::models::{Crate, CrateOwner, Owner, OwnerKind};
139
use crate::schema::{crate_owners, teams};
1410

1511
/// For now, just a Github Team. Can be upgraded to other teams
@@ -58,65 +54,6 @@ impl NewTeam<'_> {
5854
}
5955

6056
impl Team {
61-
/// Tries to create or update a Github Team. Assumes `org` and `team` are
62-
/// correctly parsed out of the full `name`. `name` is passed as a
63-
/// convenience to avoid rebuilding it.
64-
pub async fn create_or_update_github_team(
65-
gh_client: &dyn GitHubClient,
66-
conn: &mut AsyncPgConnection,
67-
login: &str,
68-
org_name: &str,
69-
team_name: &str,
70-
req_user: &User,
71-
) -> AppResult<Self> {
72-
// GET orgs/:org/teams
73-
// check that `team` is the `slug` in results, and grab its data
74-
75-
// "sanitization"
76-
fn is_allowed_char(c: char) -> bool {
77-
matches!(c, 'a'..='z' | 'A'..='Z' | '0'..='9' | '-' | '_')
78-
}
79-
80-
if let Some(c) = org_name.chars().find(|c| !is_allowed_char(*c)) {
81-
return Err(bad_request(format_args!(
82-
"organization cannot contain special \
83-
characters like {c}"
84-
)));
85-
}
86-
87-
let token = AccessToken::new(req_user.gh_access_token.expose_secret().to_string());
88-
let team = gh_client.team_by_name(org_name, team_name, &token).await
89-
.map_err(|_| {
90-
bad_request(format_args!(
91-
"could not find the github team {org_name}/{team_name}. \
92-
Make sure that you have the right permissions in GitHub. \
93-
See https://doc.rust-lang.org/cargo/reference/publishing.html#github-permissions"
94-
))
95-
})?;
96-
97-
let org_id = team.organization.id;
98-
99-
if !can_add_team(gh_client, org_id, team.id, &req_user.gh_login, &token).await? {
100-
return Err(custom(
101-
StatusCode::FORBIDDEN,
102-
"only members of a team or organization owners can add it as an owner",
103-
));
104-
}
105-
106-
let org = gh_client.org_by_name(org_name, &token).await?;
107-
108-
NewTeam::builder()
109-
.login(&login.to_lowercase())
110-
.org_id(org_id)
111-
.github_id(team.id)
112-
.maybe_name(team.name.as_deref())
113-
.maybe_avatar(org.avatar_url.as_deref())
114-
.build()
115-
.create_or_update(conn)
116-
.await
117-
.map_err(Into::into)
118-
}
119-
12057
/// Phones home to Github to ask if this User is a member of the given team.
12158
/// Note that we're assuming that the given user is the one interested in
12259
/// the answer. If this is not the case, then we could accidentally leak
@@ -145,7 +82,7 @@ impl Team {
14582
}
14683
}
14784

148-
async fn can_add_team(
85+
pub async fn can_add_team(
14986
gh_client: &dyn GitHubClient,
15087
org_id: i32,
15188
team_id: i32,

0 commit comments

Comments
 (0)