member.rs
· 5.2 KiB · Rust
Исходник
use kittycat::perms::Permission;
use serenity::all::{GuildId, RoleId, UserId};
/// Rederive permissions rederives the permissions given a member id and a list of roles
///
/// Calling rederive_perms has some side-effects
///
/// 0. The member will automatically be added to the guild_members table if they are not already in it
/// 1. Resolved_perms_cache will be updated in the guild_members table
pub async fn rederive_perms(
pool: &sqlx::PgPool,
guild_id: GuildId,
user_id: UserId,
roles: &[RoleId],
) -> Result<Vec<Permission>, crate::Error> {
let roles_str = {
let mut r = Vec::new();
for role in roles {
r.push(role.to_string());
}
r.push(guild_id.everyone_role().to_string());
r
};
let mut tx = pool.begin().await?;
let rec = sqlx::query!(
"SELECT perm_overrides FROM guild_members WHERE guild_id = $1 AND user_id = $2",
guild_id.to_string(),
user_id.to_string()
)
.fetch_optional(&mut *tx)
.await?;
// Rederive permissions for the new perms
let role_perms = sqlx::query!(
"SELECT role_id, perms, index FROM guild_roles WHERE guild_id = $1 AND role_id = ANY($2)",
guild_id.to_string(),
&roles_str
)
.fetch_all(&mut *tx)
.await?;
let mut user_positions = Vec::new();
for role in role_perms {
user_positions.push(kittycat::perms::PartialStaffPosition {
id: role.role_id,
perms: role
.perms
.iter()
.map(|x| Permission::from_string(x))
.collect(),
index: role.index,
})
}
let (in_db, perm_overrides) = if let Some(rec) = rec {
(
true,
rec.perm_overrides
.iter()
.map(|x| Permission::from_string(x))
.collect(),
)
} else {
(false, Vec::new())
};
let resolved_perms = kittycat::perms::StaffPermissions {
user_positions,
perm_overrides,
}
.resolve();
let resolved_perms_str = resolved_perms
.iter()
.map(|x| x.to_string())
.collect::<Vec<String>>();
if in_db {
sqlx::query!(
"UPDATE guild_members SET roles = $1, resolved_perms_cache = $2, needs_perm_rederive = false WHERE guild_id = $3 AND user_id = $4",
&roles_str,
&resolved_perms_str,
guild_id.to_string(),
user_id.to_string()
)
.execute(&mut *tx)
.await?;
} else {
// Check if guild is in the guilds table
let guild_exists = sqlx::query!(
"SELECT COUNT(*) FROM guilds WHERE id = $1",
guild_id.to_string()
)
.fetch_one(&mut *tx)
.await?;
if guild_exists.count.unwrap_or_default() == 0 {
sqlx::query!("INSERT INTO guilds (id) VALUES ($1)", guild_id.to_string())
.execute(&mut *tx)
.await?;
}
sqlx::query!(
"INSERT INTO guild_members (guild_id, user_id, roles, resolved_perms_cache) VALUES ($1, $2, $3, $4)",
guild_id.to_string(),
user_id.to_string(),
&roles_str,
&resolved_perms_str
)
.execute(&mut *tx)
.await?;
}
tx.commit().await?;
Ok(resolved_perms)
}
/// Returns the kittycat permissions of a user. This function also takes into account permission overrides etc.
pub async fn get_kittycat_perms(
pool: &sqlx::PgPool,
guild_id: GuildId,
guild_owner_id: UserId,
user_id: UserId,
roles: &[RoleId],
) -> Result<Vec<Permission>, crate::Error> {
// For now, owners have full permission, this may change in the future (maybe??)
if guild_owner_id == user_id {
return Ok(vec!["global.*".into()]);
}
let everyone_role = guild_id.everyone_role();
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())
.fetch_optional(pool)
.await?;
if let Some(rec) = rec {
if rec.needs_perm_rederive {
return rederive_perms(pool, guild_id, user_id, roles).await;
}
// Check user roles against db roles
let db_roles = rec.roles;
let mut roles_changed = false;
for role in roles {
if !db_roles.contains(&role.to_string()) {
roles_changed = true;
break;
}
}
// Check everyone role too
if !db_roles.contains(&everyone_role.to_string()) {
roles_changed = true;
}
if !roles_changed {
Ok(rec
.resolved_perms_cache
.iter()
.map(|x| Permission::from_string(x))
.collect::<Vec<Permission>>()) // Then use the resolved perms cache
} else {
Ok(rederive_perms(pool, guild_id, user_id, roles).await?)
}
} else {
// They have no column in db, we cannot have a fast-path as the everyone role may have permissions
Ok(rederive_perms(pool, guild_id, user_id, roles).await?)
}
}
1 | use kittycat::perms::Permission; |
2 | use 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 |
10 | pub 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. |
126 | pub 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 |