-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathpermissions.py
More file actions
293 lines (233 loc) · 10 KB
/
permissions.py
File metadata and controls
293 lines (233 loc) · 10 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
"""
Permission management system for BunBot favorites.
Handles role-based permissions with hierarchical levels.
"""
import logging
from typing import List, Dict, Any, Optional
import discord
from database import get_database
logger = logging.getLogger('discord')
class PermissionManager:
"""Manages role-based permissions for favorites system"""
def __init__(self):
self.db = get_database()
def get_user_permission_level(self, guild_id: int, user: discord.Member) -> int:
"""
Get highest permission level for user based on their roles
Args:
guild_id: Discord guild ID
user: Discord member object
Returns:
Highest permission level (1=user, 2=dj, 3=radio manager, 4=admin)
"""
max_level = 1 # Default 'user' level
try:
# Check each role the user has
for role in user.roles:
level = self.get_role_permission_level(guild_id, role.id)
max_level = max(max_level, level)
logger.debug(f"User {user.id} in guild {guild_id} has permission level {max_level}")
return max_level
except Exception as e:
logger.error(f"Error getting permission level for user {user.id}: {e}")
return 1 # Default to user level on error
def get_role_permission_level(self, guild_id: int, role_id: int) -> int:
"""
Get permission level for a specific Discord role
Args:
guild_id: Discord guild ID
role_id: Discord role ID
Returns:
Permission level for the role (1 if not found)
"""
try:
# Query server_roles to get the role mapping
results = self.db.execute_query("""
SELECT rh.permission_level
FROM server_roles sr
JOIN role_hierarchy rh ON sr.role_name = rh.role_name
WHERE sr.guild_id = ? AND sr.discord_role_id = ?
""", (guild_id, role_id))
if results:
return results[0]['permission_level']
return 1 # Default user level
except Exception as e:
logger.error(f"Error getting role permission level: {e}")
return 1
def has_permission(self, guild_id: int, user: discord.Member, permission: str) -> bool:
"""
Check if user has a specific permission
Args:
guild_id: Discord guild ID
user: Discord member object
permission: Permission name (can_set_favorites, can_remove_favorites, can_manage_roles)
Returns:
True if user has the permission
"""
# Whitelist valid permission columns to prevent SQL injection
valid_permissions = {
'can_set_favorites',
'can_remove_favorites',
'can_manage_roles'
}
if permission not in valid_permissions:
logger.error(f"Invalid permission requested: {permission}")
return False
try:
# Get all permissions for user's roles
role_ids = [role.id for role in user.roles]
if not role_ids:
return False
# Create placeholders for SQL IN clause
placeholders = ','.join('?' * len(role_ids))
# Use parameterized query with whitelisted column name
query = f"""
SELECT rh.{permission}
FROM server_roles sr
JOIN role_hierarchy rh ON sr.role_name = rh.role_name
WHERE sr.guild_id = ? AND sr.discord_role_id IN ({placeholders})
AND rh.{permission} = 1
"""
results = self.db.execute_query(query, (guild_id, *role_ids))
has_perm = len(results) > 0
logger.debug(f"User {user.id} permission check for {permission}: {has_perm}")
return has_perm
except Exception as e:
logger.error(f"Error checking permission {permission} for user {user.id}: {e}")
return False
def can_set_favorites(self, guild_id: int, user: discord.Member) -> bool:
"""Check if user can set favorites"""
return self.has_permission(guild_id, user, 'can_set_favorites')
def can_remove_favorites(self, guild_id: int, user: discord.Member) -> bool:
"""Check if user can remove favorites"""
return self.has_permission(guild_id, user, 'can_remove_favorites')
def can_manage_roles(self, guild_id: int, user: discord.Member) -> bool:
"""Check if user can manage role assignments"""
return self.has_permission(guild_id, user, 'can_manage_roles')
def assign_role_permission(self, guild_id: int, role_id: int, role_name: str) -> bool:
"""
Assign a permission level to a Discord role
Args:
guild_id: Discord guild ID
role_id: Discord role ID
role_name: Permission role name (user, dj, radio manager, admin)
Returns:
True if assignment was successful
"""
try:
# Check if role_name exists in hierarchy
hierarchy_check = self.db.execute_query(
"SELECT role_name FROM role_hierarchy WHERE role_name = ?",
(role_name,)
)
if not hierarchy_check:
logger.error(f"Invalid role name: {role_name}")
return False
# Insert or update the role assignment
self.db.execute_non_query("""
INSERT OR REPLACE INTO server_roles (guild_id, discord_role_id, role_name)
VALUES (?, ?, ?)
""", (guild_id, role_id, role_name))
logger.info(f"Assigned role {role_id} in guild {guild_id} to permission level {role_name}")
return True
except Exception as e:
logger.error(f"Error assigning role permission: {e}")
return False
def remove_role_permission(self, guild_id: int, role_id: int) -> bool:
"""
Remove permission assignment from a Discord role
Args:
guild_id: Discord guild ID
role_id: Discord role ID
Returns:
True if removal was successful
"""
try:
affected_rows = self.db.execute_non_query("""
DELETE FROM server_roles
WHERE guild_id = ? AND discord_role_id = ?
""", (guild_id, role_id))
success = affected_rows > 0
if success:
logger.info(f"Removed role permission for role {role_id} in guild {guild_id}")
else:
logger.warning(f"No permission found for role {role_id} in guild {guild_id}")
return success
except Exception as e:
logger.error(f"Error removing role permission: {e}")
return False
def get_server_role_assignments(self, guild_id: int) -> List[Dict[str, Any]]:
"""
Get all role assignments for a server
Args:
guild_id: Discord guild ID
Returns:
List of role assignments with permission details
"""
try:
results = self.db.execute_query("""
SELECT sr.discord_role_id, sr.role_name, rh.permission_level,
rh.can_set_favorites, rh.can_remove_favorites, rh.can_manage_roles
FROM server_roles sr
JOIN role_hierarchy rh ON sr.role_name = rh.role_name
WHERE sr.guild_id = ?
ORDER BY rh.permission_level DESC
""", (guild_id,))
return results
except Exception as e:
logger.error(f"Error getting server role assignments: {e}")
return []
def get_available_permission_roles(self) -> List[Dict[str, Any]]:
"""
Get all available permission roles from hierarchy
Returns:
List of available permission roles
"""
try:
results = self.db.execute_query("""
SELECT role_name, permission_level, can_set_favorites,
can_remove_favorites, can_manage_roles
FROM role_hierarchy
ORDER BY permission_level ASC
""")
return results
except Exception as e:
logger.error(f"Error getting available permission roles: {e}")
return []
# Global permission manager instance
_permission_manager = None
def get_permission_manager() -> PermissionManager:
"""Get global permission manager instance"""
global _permission_manager
if _permission_manager is None:
_permission_manager = PermissionManager()
return _permission_manager
# Decorator for checking permissions
def requires_permission(permission_check):
"""
Decorator to check permissions before executing a command
Args:
permission_check: Function that takes (guild_id, user) and returns bool
"""
def decorator(func):
async def wrapper(interaction: discord.Interaction, *args, **kwargs):
perm_manager = get_permission_manager()
if not permission_check(interaction.guild.id, interaction.user):
await interaction.response.send_message(
"❌ You don't have permission to use this command.",
ephemeral=True
)
return
return await func(interaction, *args, **kwargs)
return wrapper
return decorator
# Common permission check functions
def can_set_favorites_check(guild_id: int, user: discord.Member) -> bool:
"""Permission check function for setting favorites"""
return get_permission_manager().can_set_favorites(guild_id, user)
def can_remove_favorites_check(guild_id: int, user: discord.Member) -> bool:
"""Permission check function for removing favorites"""
return get_permission_manager().can_remove_favorites(guild_id, user)
def can_manage_roles_check(guild_id: int, user: discord.Member) -> bool:
"""Permission check function for managing roles"""
return get_permission_manager().can_manage_roles(guild_id, user)