/
PermissionUtil.java
461 lines (419 loc) · 21.6 KB
/
PermissionUtil.java
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
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
/*
* Copyright 2015-2016 Austin Keener & Michael Ritter
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.dv8tion.jda.core.utils;
import net.dv8tion.jda.core.Permission;
import net.dv8tion.jda.core.entities.*;
import net.dv8tion.jda.core.entities.impl.*;
import org.apache.commons.collections4.CollectionUtils;
import java.util.List;
import java.util.Map;
public class PermissionUtil
{
/**
* Checks if one given Member can interact with a 2nd given Member - in a permission sense (kick/ban/modify perms).
* This only checks the Role-Position and does not check the actual permission (kick/ban/manage_role/...)
*
* @param issuer
* The member that tries to interact with 2nd member
* @param target
* The member that is the target of the interaction
* @return
* True, if issuer can interact with target in guild
*/
public static boolean canInteract(Member issuer, Member target)
{
checkNull(issuer, "issuer member");
checkNull(target, "target member");
Guild guild = issuer.getGuild();
if (!guild.equals(target.getGuild()))
throw new IllegalArgumentException("Provided members must both be Member objects of the same Guild!");
if(guild.getOwner().equals(issuer))
return true;
if(guild.getOwner().equals(target))
return false;
List<Role> issuerRoles = issuer.getRoles();
List<Role> targetRoles = target.getRoles();
return !issuerRoles.isEmpty() && (targetRoles.isEmpty() || canInteract(issuerRoles.get(0), targetRoles.get(0)));
}
/**
* Checks if a given Member can interact with a given Role - in a permission sense (kick/ban/modify perms).
* This only checks the Role-Position and does not check the actual permission (kick/ban/manage_role/...)
*
* @param issuer
* The member that tries to interact with the role
* @param target
* The role that is the target of the interaction
* @return
* True, if issuer can interact with target
*/
public static boolean canInteract(Member issuer, Role target)
{
checkNull(issuer, "issuer member");
checkNull(target, "target role");
Guild guild = issuer.getGuild();
if (!guild.equals(target.getGuild()))
throw new IllegalArgumentException("Provided Member issuer and Role target must be from the same Guild!");
if(guild.getOwner().equals(issuer))
return true;
List<Role> issuerRoles = issuer.getRoles();
return !issuerRoles.isEmpty() && canInteract(issuerRoles.get(0), target);
}
/**
* Checks if one given Role can interact with a 2nd given Role - in a permission sense (kick/ban/modify perms).
* This only checks the Role-Position and does not check the actual permission (kick/ban/manage_role/...)
*
* @param issuer
* The role that tries to interact with 2nd role
* @param target
* The role that is the target of the interaction
* @return
* True, if issuer can interact with target
*/
public static boolean canInteract(Role issuer, Role target)
{
checkNull(issuer, "issuer role");
checkNull(target, "target role");
if(!issuer.getGuild().equals(target.getGuild()))
throw new IllegalArgumentException("The 2 Roles are not from same Guild!");
return target.getPosition() < issuer.getPosition();
}
/**
* Check whether the provided {@link net.dv8tion.jda.core.entities.Member Member} can use the specified {@link net.dv8tion.jda.core.entities.Emote Emote}.<p>
* If the specified Member is not in the emote's guild or the emote provided is fake this will return false.
* Otherwise it will check if the emote is restricted to any roles and if that is the case if the Member has one of these.
* <br><b>Note</b>: This is not checking if the issuer owns the Guild or not.
*
* @param issuer
* The member that tries to interact with the Emote
* @param emote
* The emote that is the target interaction
* @return
* True, if the issuer can interact with the emote
* @throws IllegalArgumentException
* if the specified issuer is not in the same Guild the provided target is in
*/
public static boolean canInteract(Member issuer, Emote emote)
{
checkNull(issuer, "issuer member");
checkNull(emote, "target emote");
if (!issuer.getGuild().equals(emote.getGuild()))
throw new IllegalArgumentException("The issuer and target are not in the same Guild");
return !emote.isFake() // Fake emote -> can't use
&& (emote.getRoles().isEmpty() // Emote restricted to roles -> check if the issuer has them
|| CollectionUtils.containsAny(issuer.getRoles(), emote.getRoles()));
}
/**
* Checks whether the specified {@link net.dv8tion.jda.core.entities.Emote Emote} can be used by the provided
* {@link net.dv8tion.jda.core.entities.User User} in the {@link net.dv8tion.jda.core.entities.MessageChannel MessageChannel}.<p>
*
* @param issuer
* The user that tries to interact with the Emote
* @param emote
* The emote that is the target interaction
* @param channel
* The MessageChannel this emote should be interacted within
* @return
* True, if the issuer can interact with the emote within the specified MessageChannel
* @throws IllegalArgumentException
* if the specified issuer is not in the same Guild the provided target is in
*/
public static boolean canInteract(User issuer, Emote emote, MessageChannel channel)
{
checkNull(issuer, "issuer member");
checkNull(emote, "target emote");
checkNull(channel, "target channel");
if (emote.isFake() || !emote.getGuild().isMember(issuer))
return false; // cannot use an emote if you're not in its guild
Member member = emote.getGuild().getMemberById(issuer.getId());
if (!canInteract(member, emote))
return false;
switch (channel.getType())
{
case TEXT:
TextChannel text = (TextChannel) channel;
member = text.getGuild().getMemberById(issuer.getId());
return emote.getGuild().equals(text.getGuild()) // within the same guild
|| (emote.isManaged() && checkPermission(text, member, Permission.MESSAGE_EXT_EMOJI)); // in different guild
default:
return emote.isManaged(); // In Group or Private it only needs to be managed
}
}
public static PermissionOverride getFullPermOverride()
{
PermissionOverrideImpl override = new PermissionOverrideImpl(null, null, null);
long allow = 0, deny = 0;
for (Permission permission : Permission.values())
{
if(permission != Permission.UNKNOWN)
{
allow = allow | (1 << permission.getOffset());
}
}
return override.setAllow(allow).setDeny(deny);
}
/**
* Checks to see if the {@link net.dv8tion.jda.core.entities.Member Member} has the specified {@link net.dv8tion.jda.core.Permission Permissions}
* in the specified {@link net.dv8tion.jda.core.entities.Guild Guild}. This method properly deals with Owner status.
* <p>
* <b>Note:</b> this is based on effective permissions, not literal permissions. If a member has permissions that would
* enable them to do something without the literal permission to do it, this will still return true.<br>
* Example: If a member has the {@link net.dv8tion.jda.core.Permission#ADMINISTRATOR} permission, they will be able to
* {@link net.dv8tion.jda.core.Permission#MANAGE_SERVER} as well, even without the literal permissions.
*
* @param guild
* The {@link net.dv8tion.jda.core.entities.Guild Guild} being checked.
* @param member
* The {@link net.dv8tion.jda.core.entities.Member Member} whose permissions are being checked.
* @param permissions
* The {@link net.dv8tion.jda.core.Permission Permissions} being checked for.
* @return
* True - if the {@link net.dv8tion.jda.core.entities.Member Member} effectively has the specified {@link net.dv8tion.jda.core.Permission Permissions}.
*/
public static boolean checkPermission(Guild guild, Member member, Permission... permissions)
{
checkNull(guild, "guild");
checkNull(member, "member");
checkNull(permissions, "permissions");
if (!guild.equals(member.getGuild()))
throw new IllegalArgumentException("Provided member is not in the provided guild");
List<Role> roles = member.getRoles();
if (guild.getOwner().equals(member) // Admin or owner? If yes: no need to iterate
|| guild.getPublicRole().hasPermission(Permission.ADMINISTRATOR)
|| roles.stream().anyMatch(role -> role.hasPermission(Permission.ADMINISTRATOR)))
return true;
for (Permission perm : permissions)
{
if (!guild.getPublicRole().hasPermission(perm)
&& !roles.parallelStream().anyMatch(role -> role.hasPermission(perm)))
return false;
}
return true;
}
/**
* Checks to see if the {@link net.dv8tion.jda.core.entities.Member Member} has the specified {@link net.dv8tion.jda.core.Permission Permissions}
* in the specified {@link net.dv8tion.jda.core.entities.Channel Channel}. This method properly deals with
* {@link net.dv8tion.jda.core.entities.PermissionOverride PermissionOverrides} and Owner status.
* <p>
* <b>Note:</b> this is based on effective permissions, not literal permissions. If a member has permissions that would
* enable them to do something without the literal permission to do it, this will still return true.<br>
* Example: If a member has the {@link net.dv8tion.jda.core.Permission#ADMINISTRATOR} permission, they will be able to
* {@link net.dv8tion.jda.core.Permission#MESSAGE_WRITE} in every channel.
*
* @param member
* The {@link net.dv8tion.jda.core.entities.Member Member} whose permissions are being checked.
* @param channel
* The {@link net.dv8tion.jda.core.entities.Channel Channel} being checked.
* @param permissions
* The {@link net.dv8tion.jda.core.Permission Permissions} being checked for.
* @return
* True - if the {@link net.dv8tion.jda.core.entities.Member Member} effectively has the specified {@link net.dv8tion.jda.core.Permission Permissions}.
*/
public static boolean checkPermission(Channel channel, Member member, Permission... permissions)
{
checkNull(channel, "channel");
checkNull(member, "member");
checkNull(permissions, "permissions");
GuildImpl guild = (GuildImpl) channel.getGuild();
if (!guild.equals(member.getGuild()))
throw new IllegalArgumentException("Provided channel and member are not from the same guild!");
if (guild.getOwner().equals(member) // Admin or owner? If yes: no need to iterate
|| guild.getPublicRole().hasPermission(Permission.ADMINISTRATOR)
|| member.getRoles().stream().anyMatch(role -> role.hasPermission(Permission.ADMINISTRATOR)))
return true;
if (channel instanceof TextChannel)
{
for (Permission perm : permissions)
{
if (!checkPermission(member, perm, ((GuildImpl) channel.getGuild()),
((TextChannelImpl) channel).getRoleOverrideMap(), ((TextChannelImpl) channel).getMemberOverrideMap()))
return false;
}
}
else
{
for (Permission perm : permissions)
{
if (!checkPermission(member, perm, ((GuildImpl) channel.getGuild()),
((VoiceChannelImpl) channel).getRoleOverrideMap(), ((VoiceChannelImpl) channel).getMemberOverrideMap()))
return false;
}
}
return true;
}
/**
* Gets the <code>int</code> representation of the effective permissions allowed for this {@link net.dv8tion.jda.core.entities.Member Member}
* in this {@link net.dv8tion.jda.core.entities.Guild Guild}. This can be used in conjunction with
* {@link net.dv8tion.jda.core.Permission#getPermissions(long) Permission.getPermissions(int)} to easily get a list of all
* {@link net.dv8tion.jda.core.Permission Permissions} that this member has in this {@link net.dv8tion.jda.core.entities.Guild Guild}.
* <p>
* <b>This only returns the Guild-level permissions!</b>
*
* @param guild
* The {@link net.dv8tion.jda.core.entities.Guild Guild} being checked.
* @param member
* The {@link net.dv8tion.jda.core.entities.Member Member} whose permissions are being checked.
* @return
* The <code>long</code> representation of the literal permissions that this {@link net.dv8tion.jda.core.entities.Member Member} has in this {@link net.dv8tion.jda.core.entities.Guild Guild}.
*/
public static long getEffectivePermission(Guild guild, Member member)
{
checkNull(guild, "guild");
checkNull(member, "member");
if (!member.getGuild().equals(guild))
throw new IllegalArgumentException("Provided member is not in the provided guild!");
//Default to binary OR of all global permissions in this guild
long permission = guild.getPublicRole().getPermissionsRaw();
for (Role role : member.getRoles())
{
permission = permission | role.getPermissionsRaw();
}
return permission;
}
/**
* Gets the <code>long</code> representation of the effective permissions allowed for this {@link net.dv8tion.jda.core.entities.Member Member}
* in this {@link net.dv8tion.jda.core.entities.Channel Channel}. This can be used in conjunction with
* {@link net.dv8tion.jda.core.Permission#getPermissions(long) Permission.getPermissions(long)} to easily get a list of all
* {@link net.dv8tion.jda.core.Permission Permissions} that this member can use in this {@link net.dv8tion.jda.core.entities.Channel Channel}.<br>
* This functions very similarly to how {@link net.dv8tion.jda.core.entities.Role#getPermissionsRaw() Role.getPermissionsRaw()}.
*
* @param channel
* The {@link net.dv8tion.jda.core.entities.Channel Channel} being checked.
* @param member
* The {@link net.dv8tion.jda.core.entities.Member Member} whose permissions are being checked.
* @return
* The <code>long</code> representation of the effective permissions that this {@link net.dv8tion.jda.core.entities.Member Member}
* has in this {@link net.dv8tion.jda.core.entities.Channel Channel}.
*/
public static long getEffectivePermission(Channel channel, Member member)
{
checkNull(channel, "channel");
checkNull(member, "member");
if (!channel.getGuild().equals(member.getGuild()))
throw new IllegalArgumentException("Provided channel and provided member are not of the same guild!");
if (channel instanceof TextChannel)
{
return getEffectivePermission(member, ((GuildImpl) channel.getGuild()),
((TextChannelImpl) channel).getRoleOverrideMap(), ((TextChannelImpl) channel).getMemberOverrideMap());
}
else
{
return getEffectivePermission(member, ((GuildImpl) channel.getGuild()),
((VoiceChannelImpl) channel).getRoleOverrideMap(), ((VoiceChannelImpl) channel).getMemberOverrideMap());
}
}
/**
* Gets the <code>long</code> representation of the effective permissions allowed for this {@link net.dv8tion.jda.core.entities.Role Role}
* in this {@link net.dv8tion.jda.core.entities.Channel Channel}. This can be used in conjunction with
* {@link net.dv8tion.jda.core.Permission#getPermissions(long) Permission.getPermissions(long)} to easily get a list of all
* {@link net.dv8tion.jda.core.Permission Permissions} that this role can use in this {@link net.dv8tion.jda.core.entities.Channel Channel}.<br>
*
* @param channel
* The {@link net.dv8tion.jda.core.entities.Channel Channel} in which permissions are being checked.
* @param role
* The {@link net.dv8tion.jda.core.entities.Role Role} whose permissions are being checked.
* @return
* The <code>long</code> representation of the effective permissions that this {@link net.dv8tion.jda.core.entities.Role Role}
* has in this {@link net.dv8tion.jda.core.entities.Channel Channel}
*/
public static long getEffectivePermission(Channel channel, Role role)
{
checkNull(channel, "channel");
checkNull(role, "role");
Guild guild = channel.getGuild();
if (!guild.equals(role.getGuild()))
throw new IllegalArgumentException("Provided channel and role are not of the same guild!");
long permissions = guild.getPublicRole().getPermissionsRaw() | role.getPermissionsRaw();
PermissionOverride publicOverride = channel.getPermissionOverride(guild.getPublicRole());
PermissionOverride roleOverride = channel.getPermissionOverride(role);
if (publicOverride != null)
{
permissions &= ~publicOverride.getDeniedRaw();
permissions |= publicOverride.getAllowedRaw();
}
if (roleOverride != null)
{
permissions &= ~roleOverride.getDeniedRaw();
permissions |= roleOverride.getAllowedRaw();
}
return permissions;
}
private static boolean checkPermission(Member member, Permission perm, GuildImpl guild, Map<Role, PermissionOverride> roleOverrides, Map<Member, PermissionOverride> memberOverrides)
{
//--Do we have all permissions possible? (Owner or member has ADMINISTRATOR permission)
//--If we have all permissions possible, then we will be able to see this room.
//WE DO NOT WANT TO CHECK THIS FOR CHANNELS, AS CHANNELS CAN OVERRIDE MANAGE_PERMISSIONS
// if (checkPermission(member, Permission.ADMINISTRATOR, guild))
// return true;
//BUT: WE DO WANT TO CHECK IF HE IS OWNER
if (guild.getOwner().equals(member))
return true;
long effectivePerms = getEffectivePermission(member, guild, roleOverrides, memberOverrides);
return ((effectivePerms & (1 << Permission.ADMINISTRATOR.getOffset())) | (effectivePerms & (1 << perm.getOffset()))) > 0;
}
private static long getEffectivePermission(Member member, GuildImpl guild, Map<Role, PermissionOverride> roleOverrides, Map<Member, PermissionOverride> memberOverrides)
{
long permission = getEffectivePermission(guild, member);
//override with channel-specific overrides of @everyone
PermissionOverride override = roleOverrides.get(guild.getPublicRole());
if (override != null)
{
permission = apply(permission, override.getAllowedRaw(), override.getDeniedRaw());
}
//handle role-overrides of this member in this channel (allow > disallow)
long allow = -1;
long deny = -1;
for (Role role : member.getRoles())
{
PermissionOverride po = roleOverrides.get(role);
if (po != null) //If an override exists for this role
{
if (allow == -1 || deny == -1) //If this is the first role we've encountered.
{
allow = po.getAllowedRaw(); //First role, take values from this role as the base for permission
deny = po.getDeniedRaw(); //
}
else
{
allow = po.getAllowedRaw() | allow; //Give all the stuff allowed by this Role's allow
deny = (po.getDeniedRaw() | deny) & (~allow); //Deny everything that this role denies.
// This also rewrites the previous role's denies if this role allowed those permissions.
}
}
}
if (allow != -1 && deny != -1) //If we found atleast 1 role with overrides.
{
permission = apply(permission, allow, deny);
}
//handle member-specific overrides
PermissionOverride memberOverride = memberOverrides.get(member);
if (memberOverride != null)
{
permission = apply(permission, memberOverride.getAllowedRaw(), memberOverride.getDeniedRaw());
}
return permission;
}
private static long apply(long permission, long allow, long deny)
{
permission = permission | allow; //Allow all the things that the cascade of roles allowed
permission = permission & (~deny); //Deny everything that the cascade of roles denied.
return permission;
}
private static void checkNull(Object obj, String name)
{
if (obj == null)
throw new NullPointerException("Provided " + name + " was null!");
}
}