Skip to content

Commit 561cc21

Browse files
breaking: Updated dpp::role comparison operators to now consider role IDs (#1333)
1 parent 743d9ba commit 561cc21

File tree

6 files changed

+71
-16
lines changed

6 files changed

+71
-16
lines changed

docpages/example_programs/misc/checking-member-permissions.md

+28
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,34 @@ if (c && c->get_user_permissions(member).can(dpp::p_send_messages)) {
1515
}
1616
```
1717
18+
### Role Hierarchy
19+
20+
The recommended and correct way to compare for roles in the hierarchy is using the comparison operators (`<`, `>`) on the \ref dpp::role::operator<(dpp::role, dpp::role) "dpp::role" objects themselves.
21+
Keep in mind that multiple roles can have the same position number.
22+
As a result, comparing roles by position alone can lead to subtle bugs when checking for role hierarchy.
23+
24+
For example let's say you have a ban command, and want to make sure that any issuer of the command can only ban members lower position than their own highest role.
25+
26+
```cpp
27+
bot.on_interaction_create([](const dpp::interaction_create_t& event) {
28+
dpp::snowflake target_id = std::get<dpp::snowflake>(event.get_parameter("user"));
29+
dpp::guild_member target = event.command.get_resolved_member(target_id);
30+
31+
for (dpp::snowflake issuer_role_id : event.command.member.get_roles()) {
32+
auto issuer_role = dpp::find_role(issuer_role_id);
33+
if (issuer_role == nullptr) continue;
34+
for (dpp::snowflake target_role_id : target.get_roles()) {
35+
auto target_role = dpp::find_role(target_role_id);
36+
if (target_role == nullptr) continue;
37+
if (target_role > issuer_role) {
38+
event.reply("You can't ban someone whose role is higher than yours!");
39+
return;
40+
}
41+
}
42+
}
43+
});
44+
```
45+
1846
## Permissions in Interaction Events
1947

2048
### Default Command Permissions

include/dpp/cluster.h

+4-4
Original file line numberDiff line numberDiff line change
@@ -2170,8 +2170,8 @@ class DPP_EXPORT cluster {
21702170
* @note This method supports audit log reasons set by the cluster::set_audit_reason() method.
21712171
* @param c Channel to set permissions for
21722172
* @param overwrite_id Overwrite to change (a user or role ID)
2173-
* @param allow allow permissions bitmask
2174-
* @param deny deny permissions bitmask
2173+
* @param allow Bitmask of allowed permissions (refer to enum dpp::permissions)
2174+
* @param deny Bitmask of denied permissions (refer to enum dpp::permissions)
21752175
* @param member true if the overwrite_id is a user id, false if it is a channel id
21762176
* @param callback Function to call when the API call completes.
21772177
* On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error().
@@ -2185,8 +2185,8 @@ class DPP_EXPORT cluster {
21852185
* @note This method supports audit log reasons set by the cluster::set_audit_reason() method.
21862186
* @param channel_id ID of the channel to set permissions for
21872187
* @param overwrite_id Overwrite to change (a user or role ID)
2188-
* @param allow allow permissions bitmask
2189-
* @param deny deny permissions bitmask
2188+
* @param allow Bitmask of allowed permissions (refer to enum dpp::permissions)
2189+
* @param deny Bitmask of denied permissions (refer to enum dpp::permissions)
21902190
* @param member true if the overwrite_id is a user id, false if it is a channel id
21912191
* @param callback Function to call when the API call completes.
21922192
* On success the callback will contain a dpp::confirmation object in confirmation_callback_t::value. On failure, the value is undefined and confirmation_callback_t::is_error() method will return true. You can obtain full error details with confirmation_callback_t::get_error().

include/dpp/cluster_sync_calls.h

+4-4
Original file line numberDiff line numberDiff line change
@@ -590,8 +590,8 @@ DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev
590590
* @note This method supports audit log reasons set by the cluster::set_audit_reason() method.
591591
* @param c Channel to set permissions for
592592
* @param overwrite_id Overwrite to change (a user or role ID)
593-
* @param allow allow permissions bitmask
594-
* @param deny deny permissions bitmask
593+
* @param allow Bitmask of allowed permissions (refer to enum dpp::permissions)
594+
* @param deny Bitmask of denied permissions (refer to enum dpp::permissions)
595595
* @param member true if the overwrite_id is a user id, false if it is a channel id
596596
* @return confirmation returned object on completion
597597
* \memberof dpp::cluster
@@ -610,8 +610,8 @@ DPP_DEPRECATED("Please use coroutines instead of sync functions: https://dpp.dev
610610
* @note This method supports audit log reasons set by the cluster::set_audit_reason() method.
611611
* @param channel_id ID of the channel to set permissions for
612612
* @param overwrite_id Overwrite to change (a user or role ID)
613-
* @param allow allow permissions bitmask
614-
* @param deny deny permissions bitmask
613+
* @param allow Bitmask of allowed permissions (refer to enum dpp::permissions)
614+
* @param deny Bitmask of denied permissions (refer to enum dpp::permissions)
615615
* @param member true if the overwrite_id is a user id, false if it is a channel id
616616
* @return confirmation returned object on completion
617617
* \memberof dpp::cluster

include/dpp/role.h

+12-3
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,10 @@ class DPP_EXPORT role : public managed, public json_interface<role> {
120120

121121
/**
122122
* @brief Role position.
123+
*
124+
* @warning multiple roles can have the same position number.
125+
* As a result, comparing roles by position alone can lead to subtle bugs when checking for role hierarchy.
126+
* The recommended and correct way to compare for roles in the hierarchy is using the comparison operators on the role objects themselves.
123127
*/
124128
uint8_t position{0};
125129

@@ -322,6 +326,11 @@ class DPP_EXPORT role : public managed, public json_interface<role> {
322326
* @return true if lhs is less than rhs
323327
*/
324328
friend inline bool operator< (const role& lhs, const role& rhs) {
329+
if (lhs.position == rhs.position) {
330+
// the higher the IDs are, the lower the position
331+
return lhs.id > rhs.id;
332+
}
333+
325334
return lhs.position < rhs.position;
326335
}
327336

@@ -333,7 +342,7 @@ class DPP_EXPORT role : public managed, public json_interface<role> {
333342
* @return true if lhs is greater than rhs
334343
*/
335344
friend inline bool operator> (const role& lhs, const role& rhs) {
336-
return lhs.position > rhs.position;
345+
return !(lhs < rhs);
337346
}
338347

339348
/**
@@ -343,7 +352,7 @@ class DPP_EXPORT role : public managed, public json_interface<role> {
343352
* @return true if is equal to other
344353
*/
345354
inline bool operator== (const role& other) const {
346-
return this->position == other.position;
355+
return this->id == other.id;
347356
}
348357

349358
/**
@@ -353,7 +362,7 @@ class DPP_EXPORT role : public managed, public json_interface<role> {
353362
* @return true if is not equal to other
354363
*/
355364
inline bool operator!= (const role& other) const {
356-
return this->position != other.position;
365+
return this->id != other.id;
357366
}
358367

359368
/**

src/unittest/test.cpp

+22-5
Original file line numberDiff line numberDiff line change
@@ -342,11 +342,28 @@ Markdown lol \\|\\|spoiler\\|\\| \\~\\~strikethrough\\~\\~ \\`small \\*code\\* b
342342
set_test(TIMESTAMPTOSTRING, false);
343343
set_test(TIMESTAMPTOSTRING, dpp::ts_to_string(1642611864) == "2022-01-19T17:04:24Z");
344344

345-
set_test(ROLE_COMPARE, false);
346-
dpp::role role_1, role_2;
347-
role_1.position = 1;
348-
role_2.position = 2;
349-
set_test(ROLE_COMPARE, role_1 < role_2 && role_1 != role_2);
345+
{
346+
set_test(ROLE_COMPARE, false);
347+
dpp::role role_1, role_2;
348+
role_1.id = 99;
349+
role_1.position = 1;
350+
role_1.guild_id = 123;
351+
role_1.id = 98;
352+
role_2.position = 2;
353+
role_2.guild_id = 123;
354+
set_test(ROLE_COMPARE, role_1 < role_2 && !(role_1 > role_2) && role_1 != role_2);
355+
}
356+
{
357+
set_test(ROLE_COMPARE_CONSIDERING_ID, false);
358+
dpp::role role_1, role_2;
359+
role_1.id = 99;
360+
role_1.position = 2;
361+
role_1.guild_id = 123;
362+
role_2.id = 98;
363+
role_2.position = 2;
364+
role_2.guild_id = 123;
365+
set_test(ROLE_COMPARE_CONSIDERING_ID, role_1 < role_2 && !(role_1 > role_2));
366+
}
350367

351368
set_test(WEBHOOK, false);
352369
try {

src/unittest/test.h

+1
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@ DPP_TEST(UTILITY_CDN_ENDPOINT_URL_HASH, "utility::cdn_endpoint_url_hash", tf_off
206206
DPP_TEST(STICKER_GET_URL, "sticker::get_url aka utility::cdn_endpoint_url_sticker", tf_offline);
207207
DPP_TEST(EMOJI_GET_URL, "emoji::get_url", tf_offline);
208208
DPP_TEST(ROLE_COMPARE, "role::operator<", tf_offline);
209+
DPP_TEST(ROLE_COMPARE_CONSIDERING_ID, "role::operator<", tf_offline);
209210
DPP_TEST(ROLE_CREATE, "cluster::role_create", tf_online);
210211
DPP_TEST(ROLE_EDIT, "cluster::role_edit", tf_online);
211212
DPP_TEST(ROLE_DELETE, "cluster::role_delete", tf_online);

0 commit comments

Comments
 (0)