Last active 1723995325

member.rs Raw
1use kittycat::perms::Permission;
2use serenity::all::{GuildId, RoleId, UserId};
3
4/// Rederive permissions rederives the permissions given a member id and a list of roles
5///
6/// Calling rederive_perms has some side-effects
7///
8/// 0. The member will automatically be added to the guild_members table if they are not already in it
9/// 1. Resolved_perms_cache will be updated in the guild_members table
10pub async fn rederive_perms(
11 pool: &sqlx::PgPool,
12 guild_id: GuildId,
13 user_id: UserId,
14 roles: &[RoleId],
15) -> Result<Vec<Permission>, crate::Error> {
16 let roles_str = {
17 let mut r = Vec::new();
18
19 for role in roles {
20 r.push(role.to_string());
21 }
22
23 r.push(guild_id.everyone_role().to_string());
24
25 r
26 };
27
28 let mut tx = pool.begin().await?;
29
30 let rec = sqlx::query!(
31 "SELECT perm_overrides FROM guild_members WHERE guild_id = $1 AND user_id = $2",
32 guild_id.to_string(),
33 user_id.to_string()
34 )
35 .fetch_optional(&mut *tx)
36 .await?;
37
38 // Rederive permissions for the new perms
39 let role_perms = sqlx::query!(
40 "SELECT role_id, perms, index FROM guild_roles WHERE guild_id = $1 AND role_id = ANY($2)",
41 guild_id.to_string(),
42 &roles_str
43 )
44 .fetch_all(&mut *tx)
45 .await?;
46
47 let mut user_positions = Vec::new();
48
49 for role in role_perms {
50 user_positions.push(kittycat::perms::PartialStaffPosition {
51 id: role.role_id,
52 perms: role
53 .perms
54 .iter()
55 .map(|x| Permission::from_string(x))
56 .collect(),
57 index: role.index,
58 })
59 }
60
61 let (in_db, perm_overrides) = if let Some(rec) = rec {
62 (
63 true,
64 rec.perm_overrides
65 .iter()
66 .map(|x| Permission::from_string(x))
67 .collect(),
68 )
69 } else {
70 (false, Vec::new())
71 };
72
73 let resolved_perms = kittycat::perms::StaffPermissions {
74 user_positions,
75 perm_overrides,
76 }
77 .resolve();
78
79 let resolved_perms_str = resolved_perms
80 .iter()
81 .map(|x| x.to_string())
82 .collect::<Vec<String>>();
83
84 if in_db {
85 sqlx::query!(
86 "UPDATE guild_members SET roles = $1, resolved_perms_cache = $2, needs_perm_rederive = false WHERE guild_id = $3 AND user_id = $4",
87 &roles_str,
88 &resolved_perms_str,
89 guild_id.to_string(),
90 user_id.to_string()
91 )
92 .execute(&mut *tx)
93 .await?;
94 } else {
95 // Check if guild is in the guilds table
96 let guild_exists = sqlx::query!(
97 "SELECT COUNT(*) FROM guilds WHERE id = $1",
98 guild_id.to_string()
99 )
100 .fetch_one(&mut *tx)
101 .await?;
102
103 if guild_exists.count.unwrap_or_default() == 0 {
104 sqlx::query!("INSERT INTO guilds (id) VALUES ($1)", guild_id.to_string())
105 .execute(&mut *tx)
106 .await?;
107 }
108
109 sqlx::query!(
110 "INSERT INTO guild_members (guild_id, user_id, roles, resolved_perms_cache) VALUES ($1, $2, $3, $4)",
111 guild_id.to_string(),
112 user_id.to_string(),
113 &roles_str,
114 &resolved_perms_str
115 )
116 .execute(&mut *tx)
117 .await?;
118 }
119
120 tx.commit().await?;
121
122 Ok(resolved_perms)
123}
124
125/// Returns the kittycat permissions of a user. This function also takes into account permission overrides etc.
126pub async fn get_kittycat_perms(
127 pool: &sqlx::PgPool,
128 guild_id: GuildId,
129 guild_owner_id: UserId,
130 user_id: UserId,
131 roles: &[RoleId],
132) -> Result<Vec<Permission>, crate::Error> {
133 // For now, owners have full permission, this may change in the future (maybe??)
134 if guild_owner_id == user_id {
135 return Ok(vec!["global.*".into()]);
136 }
137
138 let everyone_role = guild_id.everyone_role();
139
140 let rec = sqlx::query!("SELECT roles, needs_perm_rederive, resolved_perms_cache, perm_overrides FROM guild_members WHERE guild_id = $1 AND user_id = $2", guild_id.to_string(), user_id.to_string())
141 .fetch_optional(pool)
142 .await?;
143
144 if let Some(rec) = rec {
145 if rec.needs_perm_rederive {
146 return rederive_perms(pool, guild_id, user_id, roles).await;
147 }
148
149 // Check user roles against db roles
150 let db_roles = rec.roles;
151
152 let mut roles_changed = false;
153
154 for role in roles {
155 if !db_roles.contains(&role.to_string()) {
156 roles_changed = true;
157 break;
158 }
159 }
160
161 // Check everyone role too
162 if !db_roles.contains(&everyone_role.to_string()) {
163 roles_changed = true;
164 }
165
166 if !roles_changed {
167 Ok(rec
168 .resolved_perms_cache
169 .iter()
170 .map(|x| Permission::from_string(x))
171 .collect::<Vec<Permission>>()) // Then use the resolved perms cache
172 } else {
173 Ok(rederive_perms(pool, guild_id, user_id, roles).await?)
174 }
175 } else {
176 // They have no column in db, we cannot have a fast-path as the everyone role may have permissions
177 Ok(rederive_perms(pool, guild_id, user_id, roles).await?)
178 }
179}
180