Skip to content

Commit 5c1f04b

Browse files
committed
fix: multiple emails cause roles to not be assigned
1 parent 80f8358 commit 5c1f04b

File tree

16 files changed

+343
-368
lines changed

16 files changed

+343
-368
lines changed

api/src/guilds/mod.rs

Lines changed: 1 addition & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
use std::collections::{HashSet};
21
use crate::AppState;
32
use crate::guilds::models::Guild;
43
use axum::extract::State;
@@ -15,20 +14,17 @@ use twilight_model::id::Id;
1514
use twilight_model::id::marker::GuildMarker;
1615
use twilight_model::user::{CurrentUser, CurrentUserGuild};
1716
use crate::discord::{get_guild, get_guild_member};
18-
use crate::guilds::tasks::{add_role_to_guild, remove_role_from_guild};
19-
use crate::guilds::verify::models::VerifyRole;
2017
use crate::utils::{admin_guilds, is_client_admin_guild};
2118

2219
pub mod models;
2320
pub mod verify;
2421
pub(crate) mod utils;
25-
pub mod tasks;
2622
pub(crate) mod votes;
2723

2824
pub fn router() -> Router<AppState> {
2925
Router::new()
3026
.route("/", get(get_guilds).post(post_guilds))
31-
.route("/{guild_id}", get(get_guilds_id).put(put_guilds_id).post(post_guilds_id))
27+
.route("/{guild_id}", get(get_guilds_id).post(post_guilds_id))
3228
.nest("/{guild_id}/verify", verify::controllers::router())
3329
.nest("/{guild_id}/votes", votes::controllers::router())
3430
.layer(CorsLayer::permissive())
@@ -134,36 +130,3 @@ async fn get_guilds_id(
134130
}
135131
})))
136132
}
137-
138-
async fn put_guilds_id(
139-
Path(guild_id): Path<Id<GuildMarker>>,
140-
Extension(current_user): Extension<CurrentUser>,
141-
State(app_state): State<AppState>,
142-
Json(guild_req): Json<Guild>,
143-
) -> Result<Json<Value>, StatusCode> {
144-
if !is_client_admin_guild(guild_id, &current_user, &app_state.discord_bot).await? {
145-
return Err(StatusCode::UNAUTHORIZED);
146-
}
147-
148-
let mut old_guild = match Guild::from_db(guild_id, &app_state.dynamo).await {
149-
Some(g) => g,
150-
None => return Err(StatusCode::NOT_FOUND),
151-
};
152-
153-
let old_set: HashSet<VerifyRole> = HashSet::from_iter(old_guild.verify.roles.clone());
154-
let new_set: HashSet<VerifyRole> = HashSet::from_iter(guild_req.verify.roles);
155-
156-
let add_roles: HashSet<VerifyRole> = new_set.difference(&old_set).cloned().collect();
157-
let remove_roles: HashSet<VerifyRole> = old_set.difference(&new_set).cloned().collect();
158-
159-
for role in add_roles {
160-
add_role_to_guild(&mut old_guild, role, &app_state.discord_bot).await?;
161-
}
162-
163-
for role in remove_roles {
164-
remove_role_from_guild(&mut old_guild, role.role_id, &app_state.discord_bot).await?;
165-
}
166-
167-
old_guild.save(&app_state.dynamo).await;
168-
Ok(Json(json!(old_guild)))
169-
}

api/src/guilds/tasks.rs

Lines changed: 0 additions & 97 deletions
This file was deleted.
Lines changed: 125 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,134 @@
1+
use std::sync::Arc;
2+
use axum::extract::{Path, State};
13
use crate::AppState;
2-
use axum::Json;
3-
use axum::routing::get;
4+
use axum::{Extension, Json};
5+
use axum::routing::{post, put};
6+
use http::StatusCode;
7+
use lambda_http::tracing::info;
8+
use serde::{Deserialize, Serialize};
49
use serde_json::{Value, json};
510
use tower_http::cors::CorsLayer;
11+
use twilight_model::id::Id;
12+
use twilight_model::id::marker::{GuildMarker, RoleMarker};
13+
use twilight_model::user::CurrentUser;
14+
use crate::discord::{add_guild_member_role, remove_guild_member_role};
15+
use crate::guilds::models::Guild;
16+
use crate::guilds::verify::models::{Verify, VerifyRole};
17+
use crate::users::utils::link_arr_match;
18+
use crate::utils::is_client_admin_guild;
619

720
pub fn router() -> axum::Router<AppState> {
8-
axum::Router::new().route("/", get(get_verify))
21+
axum::Router::new()
22+
.route("/recon", post(post_recon))
23+
.route("/roles/{role_id}", put(put_roles_id).delete(delete_roles_id))
924
.layer(CorsLayer::permissive())
1025
}
1126

12-
async fn get_verify() -> Json<Value> {
13-
Json(json!({ "msg": "I am GET /verify" }))
27+
#[derive(Serialize, Deserialize)]
28+
struct PutRoleRequest {
29+
pub pattern: String,
1430
}
31+
32+
async fn put_roles_id(
33+
Path((guild_id,role_id)): Path<(Id<GuildMarker>,Id<RoleMarker>)>,
34+
Extension(current_user): Extension<CurrentUser>,
35+
State(app_state): State<AppState>,
36+
Json(put_role_request): Json<PutRoleRequest>
37+
) -> Result<Json<Value>, StatusCode> {
38+
if !is_client_admin_guild(guild_id, &current_user, &app_state.discord_bot).await? {
39+
return Err(StatusCode::FORBIDDEN);
40+
}
41+
42+
let mut guild = Guild::from_db(guild_id, &app_state.dynamo).await.unwrap();
43+
44+
if guild.verify.roles.iter().any(|r| r.role_id == role_id) {
45+
remove_existing_role(&mut guild, role_id, &app_state).await?;
46+
}
47+
48+
let mut new_role = VerifyRole {
49+
role_id,
50+
pattern: put_role_request.pattern,
51+
..Default::default()
52+
};
53+
54+
for (user_id, user_links) in &guild.verify.user_links {
55+
if link_arr_match(user_links, &new_role.pattern) {
56+
add_guild_member_role(guild.guild_id, *user_id, role_id, &app_state.discord_bot).await?;
57+
new_role.members += 1;
58+
}
59+
}
60+
guild.verify.roles.push(new_role);
61+
guild.save(&app_state.dynamo).await;
62+
63+
Ok(Json(json!(guild.verify.roles.iter().find(|r| r.role_id == role_id).unwrap())))
64+
}
65+
66+
67+
async fn delete_roles_id(
68+
Path((guild_id,role_id)): Path<(Id<GuildMarker>,Id<RoleMarker>)>,
69+
Extension(current_user): Extension<CurrentUser>,
70+
State(app_state): State<AppState>,
71+
) -> Result<StatusCode, StatusCode> {
72+
if !is_client_admin_guild(guild_id, &current_user, &app_state.discord_bot).await? {
73+
return Err(StatusCode::FORBIDDEN);
74+
}
75+
76+
let mut guild = Guild::from_db(guild_id, &app_state.dynamo).await.unwrap();
77+
78+
remove_existing_role(&mut guild, role_id, &app_state).await?;
79+
80+
guild.save(&app_state.dynamo).await;
81+
82+
Ok(StatusCode::NO_CONTENT)
83+
}
84+
85+
async fn remove_existing_role(
86+
guild: &mut Guild,
87+
role_id: Id<RoleMarker>,
88+
app_state: &AppState
89+
) -> Result<(), StatusCode> {
90+
let existing_role_opt = guild.verify.roles.iter().find(|r| r.role_id == role_id);
91+
92+
if existing_role_opt.is_none() {
93+
// No role found
94+
return Ok(())
95+
}
96+
let existing_role = existing_role_opt.unwrap();
97+
98+
for (user_id, user_links) in &guild.verify.user_links {
99+
if link_arr_match(user_links, &existing_role.pattern) {
100+
remove_guild_member_role(guild.guild_id, *user_id, role_id, &app_state.discord_bot).await?
101+
}
102+
}
103+
104+
guild.verify.roles.retain(|r| r.role_id != role_id);
105+
Ok(())
106+
}
107+
108+
async fn post_recon(
109+
Path(guild_id): Path<Id<GuildMarker>>,
110+
Extension(discord_user): Extension<Arc<twilight_http::Client>>,
111+
State(app_state): State<AppState>,
112+
) -> Result<StatusCode, StatusCode> {
113+
if discord_user.token() != app_state.discord_bot.token() {
114+
return Err(StatusCode::FORBIDDEN);
115+
}
116+
117+
let mut guild = Guild::from_db(guild_id, &app_state.dynamo).await.unwrap();
118+
let Verify{roles, user_links} = &mut guild.verify;
119+
120+
for role in roles {
121+
role.members = 0;
122+
for (user_id, links) in &*user_links {
123+
if link_arr_match(links, &role.pattern) {
124+
add_guild_member_role(guild.guild_id, *user_id, role.role_id, &app_state.discord_bot).await?;
125+
role.members += 1;
126+
} else {
127+
remove_guild_member_role(guild.guild_id, *user_id, role.role_id, &app_state.discord_bot).await?;
128+
}
129+
}
130+
}
131+
guild.save(&app_state.dynamo).await;
132+
133+
Ok(StatusCode::NO_CONTENT)
134+
}

api/src/guilds/verify/models.rs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,26 @@ use aws_sdk_dynamodb::types::AttributeValue;
44
use serde::{Deserialize, Serialize};
55
use twilight_model::id::Id;
66
use twilight_model::id::marker::{RoleMarker, UserMarker};
7-
use crate::dynamo::{as_map, as_map_vec, as_string, as_string_opt, as_u32};
7+
use crate::dynamo::{as_map, as_map_vec, as_string, as_u32};
88
use crate::users::models::Link;
99

1010
#[derive(Clone, Serialize, Deserialize, PartialEq, Eq)]
1111
pub struct VerifyRole {
1212
pub role_id: Id<RoleMarker>,
13-
pub role_name: Option<String>,
1413
pub pattern: String,
1514
pub members: u32,
1615
}
1716

17+
impl Default for VerifyRole {
18+
fn default() -> Self {
19+
VerifyRole {
20+
role_id: Id::new(1),
21+
pattern: "".to_string(),
22+
members: 0,
23+
}
24+
}
25+
}
26+
1827
impl Hash for VerifyRole {
1928
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
2029
self.role_id.hash(state);
@@ -30,7 +39,6 @@ impl From<&HashMap<String, AttributeValue>> for VerifyRole {
3039
.parse::<u64>()
3140
.unwrap_or(0),
3241
),
33-
role_name: as_string_opt(item.get("role_name")),
3442
pattern: as_string(item.get("pattern"), &"".to_string()),
3543
members: as_u32(item.get("members"), 0),
3644
}
@@ -41,9 +49,6 @@ impl From<VerifyRole> for HashMap<String, AttributeValue> {
4149
fn from(role: VerifyRole) -> Self {
4250
let mut role_map = HashMap::new();
4351
role_map.insert("role_id".to_string(), AttributeValue::S(role.role_id.to_string()));
44-
if let Some(role_name) = role.role_name {
45-
role_map.insert("role_name".to_string(), AttributeValue::S(role_name));
46-
}
4752
role_map.insert("pattern".to_string(), AttributeValue::S(role.pattern));
4853
role_map.insert("members".to_string(), AttributeValue::N(role.members.to_string()));
4954
role_map

api/src/middleware.rs

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,30 @@ pub async fn auth_middleware(
1919

2020
let auth_header = headers.get("Authorization").unwrap().to_str().unwrap();
2121
let (scheme, credentials) = auth_header.split_once(' ').unwrap();
22-
if scheme == "Discord" {
23-
let client = Client::new(format!("Bearer {credentials}"));
24-
let current_user = get_current_user(&client).await?;
25-
let ext_mut = request.extensions_mut();
26-
ext_mut.insert(std::sync::Arc::new(client));
27-
ext_mut.insert(current_user);
28-
Ok(next.run(request).await)
29-
} else {
30-
Err(StatusCode::UNAUTHORIZED)
22+
match scheme {
23+
"Discord" => {
24+
let client = Client::new(format!("Bearer {credentials}"));
25+
let current_user = get_current_user(&client).await?;
26+
if current_user.bot {
27+
return Err(StatusCode::UNAUTHORIZED);
28+
}
29+
let ext_mut = request.extensions_mut();
30+
ext_mut.insert(std::sync::Arc::new(client));
31+
ext_mut.insert(current_user);
32+
Ok(next.run(request).await)
33+
}
34+
"Bot" => {
35+
if credentials != std::env::var("DISCORD_BOT_TOKEN").expect("DISCORD_BOT_TOKEN must be set") {
36+
return Err(StatusCode::UNAUTHORIZED);
37+
}
38+
let client = Client::new(format!("Bot {credentials}"));
39+
let ext_mut = request.extensions_mut();
40+
ext_mut.insert(std::sync::Arc::new(client));
41+
Ok(next.run(request).await)
42+
}
43+
_ => {
44+
Err(StatusCode::UNAUTHORIZED)
45+
}
3146
}
3247
}
3348

0 commit comments

Comments
 (0)