Skip to content

Commit

Permalink
Discord division role id (#12)
Browse files Browse the repository at this point in the history
  • Loading branch information
mbund authored Oct 22, 2024
1 parent c48ff03 commit 0f3e7a2
Show file tree
Hide file tree
Showing 12 changed files with 388 additions and 63 deletions.
5 changes: 4 additions & 1 deletion examples/standalone/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,22 @@ auth:
divisions:
- name: Open
description: Open division for everyone
discord_role_id: 1297614892563435540
- name: Undergraduate
description: Undergraduate university students globally
email_regex: ^.*.edu$
requirement: Must verify a valid university .edu email address
discord_role_id: 1297614961941680239
- name: OSU
description: OSU undergraduate students
email_regex: ^.*\.\d+@(buckeyemail\.)?osu\.edu$
requirement: Must verify a valid OSU email address. Max of up to 4 players
max_players: 4
discord_role_id: 1297615021773295709
discord:
guild_id: 1160610137703186636
client_id: 1160076447977848945
author_role_id: 1198496803130183770
first_blood_channel_id: 1295827879149703299
support_channel_id: 1295827929359581337
# verified_role_id: 1170429782270431283
verified_role_id: 1170429782270431283
7 changes: 6 additions & 1 deletion rhombus/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -494,6 +494,7 @@ impl<P: Plugin + Send + Sync + 'static, U: UploadProvider + Send + Sync + 'stati
description: division.description.clone(),
max_players,
division_eligibility,
discord_role_id: division.discord_role_id,
is_default: i == 0,
}
})
Expand All @@ -508,6 +509,7 @@ impl<P: Plugin + Send + Sync + 'static, U: UploadProvider + Send + Sync + 'stati
max_players: MaxDivisionPlayers::Unlimited,
division_eligibility: Arc::new(OpenDivisionEligibilityProvider {}),
is_default: true,
discord_role_id: None,
}]
};

Expand Down Expand Up @@ -802,6 +804,8 @@ impl<P: Plugin + Send + Sync + 'static, U: UploadProvider + Send + Sync + 'stati
}
};

let divisions = Arc::new(divisions);

let ip_extractor = match self_arc.ip_extractor.clone() {
Some(ip_extractor) => Some(ip_extractor),
None => settings
Expand Down Expand Up @@ -908,6 +912,7 @@ impl<P: Plugin + Send + Sync + 'static, U: UploadProvider + Send + Sync + 'stati
cached_db.clone(),
outbound_mailer.clone(),
jinja.clone(),
divisions.clone(),
)
.await,
);
Expand Down Expand Up @@ -969,7 +974,7 @@ impl<P: Plugin + Send + Sync + 'static, U: UploadProvider + Send + Sync + 'stati
.clone()
.unwrap_or(Arc::new(default_ip_extractor)),
outbound_mailer,
divisions: Arc::new(divisions),
divisions,
router: rr.clone(),
global_page_meta,
score_type_map,
Expand Down
96 changes: 66 additions & 30 deletions rhombus/src/internal/auth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,31 @@ pub async fn route_signin(
.add_user_to_team(user.id, team_meta.id, Some(old_team.id))
.await
.unwrap();

if let (Some(bot), Some(user_discord_id)) =
(state.bot.as_ref(), user.discord_id)
{
let new_division = state
.divisions
.iter()
.find(|d| d.id == new_team.division_id)
.unwrap();
if let Some(discord_role_id) = new_division.discord_role_id {
bot.give_role_to_users(&[user_discord_id], discord_role_id)
.await;
}

let old_division = state
.divisions
.iter()
.find(|d| d.id == old_team.division_id)
.unwrap();
if let Some(discord_role_id) = old_division.discord_role_id {
bot.remove_role_from_users(&[user_discord_id], discord_role_id)
.await;
}
}

return Redirect::to("/team").into_response();
}

Expand Down Expand Up @@ -424,8 +449,6 @@ pub async fn route_signin_discord_callback(
return (StatusCode::BAD_REQUEST, Json(json_error)).into_response();
};

let bot = state.bot.as_ref().unwrap();

let client = Client::new();
let res = client
.post("https://discord.com/api/oauth2/token")
Expand Down Expand Up @@ -474,34 +497,7 @@ pub async fn route_signin_discord_callback(
panic!();
};

// join the user to the guild
let client = Client::new();
let res = client
.put(format!(
"https://discord.com/api/guilds/{}/members/{}",
discord.guild_id, profile.id
))
.header("Authorization", format!("Bot {}", discord.bot_token))
.json(&json!({
"access_token": oauth_token.access_token,
}))
.send()
.await
.unwrap();
if !res.status().is_success() {
let json_error = ErrorResponse {
message: format!("Discord returned an error: {:?}", res.text().await),
};
return (StatusCode::BAD_REQUEST, Json(json_error)).into_response();
}

let discord_id = profile.id.parse::<NonZeroU64>().unwrap();
if let Err(err) = bot.verify_user(discord_id).await {
let json_error = ErrorResponse {
message: format!("Discord returned an error: {:?}", err),
};
return (StatusCode::BAD_REQUEST, Json(json_error)).into_response();
}

let avatar = if let Some(avatar) = profile.avatar {
format!(
Expand Down Expand Up @@ -560,6 +556,46 @@ pub async fn route_signin_discord_callback(
},
};

if let Some(bot) = state.bot.as_ref() {
if let Err(err) = bot.verify_user(discord_id).await {
let json_error = ErrorResponse {
message: format!("Discord returned an error: {:?}", err),
};
return (StatusCode::BAD_REQUEST, Json(json_error)).into_response();
}

let team = state.db.get_team_from_id(team_id).await.unwrap();
let division = state
.divisions
.iter()
.find(|d| d.id == team.division_id)
.unwrap();
if let Some(discord_role_id) = division.discord_role_id {
bot.give_role_to_users(&[discord_id], discord_role_id).await;
}
}

// join the user to the guild
let client = Client::new();
let res = client
.put(format!(
"https://discord.com/api/guilds/{}/members/{}",
discord.guild_id, profile.id
))
.header("Authorization", format!("Bot {}", discord.bot_token))
.json(&json!({
"access_token": oauth_token.access_token,
}))
.send()
.await
.unwrap();
if !res.status().is_success() {
let json_error = ErrorResponse {
message: format!("Discord returned an error: {:?}", res.text().await),
};
return (StatusCode::BAD_REQUEST, Json(json_error)).into_response();
}

let unset_oauth_state_cookie = Cookie::build(("rhombus-oauth-discord", ""))
.path("/")
.removal()
Expand Down Expand Up @@ -795,7 +831,7 @@ pub async fn route_signin_ctftime(state: State<RouterState>) -> impl IntoRespons

// ctftime doesn't like dots in the state (or any non-base64url-encoded characters)
// so we replace them with a statistically unlikely sequence of underscores. This is
// then re-replaced in [[route_signin_discord_callback]]
// then re-replaced in [[route_signin_ctftime_callback]]
let signed_oauth_state =
signed_oauth_state.replace('.', "______________________________________");

Expand Down
5 changes: 3 additions & 2 deletions rhombus/src/internal/database/cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -215,11 +215,12 @@ impl Database for DbCache {
self.inner.get_user_from_discord_id(discord_id).await
}

async fn kick_user(&self, user_id: i64, team_id: i64) -> Result<()> {
async fn kick_user(&self, user_id: i64, team_id: i64) -> Result<i64> {
let result = self.inner.kick_user(user_id, team_id).await;
if result.is_ok() {
if let Ok(new_team_id) = result {
USER_CACHE.remove(&user_id);
TEAM_CACHE.remove(&team_id);
TEAM_CACHE.remove(&new_team_id);
}
result
}
Expand Down
48 changes: 36 additions & 12 deletions rhombus/src/internal/database/libsql.rs
Original file line number Diff line number Diff line change
Expand Up @@ -292,11 +292,12 @@ impl<T: ?Sized + LibSQLConnection + Send + Sync> Database for T {
.get::<i64>(0)
.unwrap();

tx.execute(
"INSERT INTO rhombus_email (email, user_id) VALUES (?1, ?2)",
params!(email, user_id),
)
.await?;
_ = tx
.execute(
"INSERT INTO rhombus_email (email, user_id) VALUES (?1, ?2)",
params!(email, user_id),
)
.await;

tx.commit().await?;
return Ok(Ok((user_id, team_id)));
Expand Down Expand Up @@ -1099,15 +1100,38 @@ impl<T: ?Sized + LibSQLConnection + Send + Sync> Database for T {
}))
}

async fn kick_user(&self, user_id: i64, _team_id: i64) -> Result<()> {
self.connect()
.await?
.execute(
"UPDATE rhombus_user SET team_id = owner_team_id WHERE id = ?1",
async fn kick_user(&self, user_id: i64, _team_id: i64) -> Result<i64> {
let tx = self.transaction().await?;

let new_team_id = tx
.query(
"
UPDATE rhombus_user
SET team_id = owner_team_id
WHERE id = ?1
RETURNING owner_team_id
",
[user_id],
)
.await?;
Ok(())
.await?
.next()
.await?
.unwrap()
.get::<i64>(0)
.unwrap();

tx.execute(
"
UPDATE rhombus_solve
SET team_id = ?2
WHERE user_id = ?1
",
[user_id, new_team_id],
)
.await?;

tx.commit().await?;
Ok(new_team_id)
}

async fn roll_invite_token(&self, team_id: i64) -> Result<String> {
Expand Down
2 changes: 1 addition & 1 deletion rhombus/src/internal/database/postgres.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ impl Database for Postgres {
todo!()
}

async fn kick_user(&self, _user_id: i64, _team_id: i64) -> Result<()> {
async fn kick_user(&self, _user_id: i64, _team_id: i64) -> Result<i64> {
todo!()
}

Expand Down
2 changes: 1 addition & 1 deletion rhombus/src/internal/database/provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,7 @@ pub trait Database {
) -> Result<()>;
async fn get_user_from_id(&self, user_id: i64) -> Result<User>;
async fn get_user_from_discord_id(&self, discord_id: NonZeroU64) -> Result<User>;
async fn kick_user(&self, user_id: i64, team_id: i64) -> Result<()>;
async fn kick_user(&self, user_id: i64, team_id: i64) -> Result<i64>;
async fn roll_invite_token(&self, team_id: i64) -> Result<String>;
async fn set_team_name(
&self,
Expand Down
Loading

0 comments on commit 0f3e7a2

Please sign in to comment.