basic permission handling + use for edit/delete

This commit is contained in:
ouwou 2020-09-24 02:15:25 -04:00
parent dbe9dc3c64
commit 8341781918
14 changed files with 251 additions and 26 deletions

View File

@ -173,6 +173,8 @@ add_executable(abaddon
discord/message.cpp
discord/invite.hpp
discord/invite.cpp
discord/permissions.hpp
discord/permissions.cpp
windows/mainwindow.hpp
windows/mainwindow.cpp
)

View File

@ -163,11 +163,12 @@ void ChatMessageItem::AttachMenuHandler(Gtk::Widget *widget) {
// clang-format on
void ChatMessageItem::ShowMenu(const GdkEvent *event) {
auto &client = Abaddon::Get().GetDiscordClient();
auto *data = client.GetMessage(ID);
bool can_manage = client.GetUserData().ID == data->Author.ID;
m_menu_delete_message->set_sensitive(can_manage);
m_menu_edit_message->set_sensitive(can_manage);
const auto &client = Abaddon::Get().GetDiscordClient();
const auto *data = client.GetMessage(ID);
const bool can_edit = client.GetUserData().ID == data->Author.ID;
const bool can_delete = can_edit || client.HasChannelPermission(client.GetUserData().ID, ChannelID, Permission::MANAGE_MESSAGES);
m_menu_delete_message->set_sensitive(can_delete);
m_menu_edit_message->set_sensitive(can_edit);
m_menu.popup_at_pointer(event);
}

View File

@ -5,7 +5,7 @@ void from_json(const nlohmann::json &j, Channel &m) {
JS_D("type", m.Type);
JS_O("guild_id", m.GuildID);
JS_O("position", m.Position);
// JS_O("permission_overwrites", m.PermissionOverwrites);
JS_O("permission_overwrites", m.PermissionOverwrites);
JS_ON("name", m.Name);
JS_ON("topic", m.Topic);
JS_O("nsfw", m.IsNSFW);
@ -20,3 +20,10 @@ void from_json(const nlohmann::json &j, Channel &m) {
JS_ON("parent_id", m.ParentID);
JS_ON("last_pin_timestamp", m.LastPinTimestamp);
}
std::optional<PermissionOverwrite> Channel::GetOverwrite(Snowflake id) const {
auto ret = std::find_if(PermissionOverwrites.begin(), PermissionOverwrites.end(), [id](const auto x) { return x.ID == id; });
if (ret != PermissionOverwrites.end())
return *ret;
return std::nullopt;
}

View File

@ -2,6 +2,8 @@
#include "snowflake.hpp"
#include "json.hpp"
#include "user.hpp"
#include "permissions.hpp"
#include <optional>
#include <string>
#include <vector>
@ -16,24 +18,26 @@ enum class ChannelType : int {
};
struct Channel {
Snowflake ID; //
ChannelType Type; //
Snowflake GuildID; // opt
int Position = -1; // opt
// std::vector<PermissionOverwriteData> PermissionOverwrites; // opt
std::string Name; // opt, null (null for dm's)
std::string Topic; // opt, null
bool IsNSFW = false; // opt
Snowflake LastMessageID; // opt, null
int Bitrate = 0; // opt
int UserLimit = 0; // opt
int RateLimitPerUser = 0; // opt
std::vector<User> Recipients; // opt
std::string Icon; // opt, null
Snowflake OwnerID; // opt
Snowflake ApplicationID; // opt
Snowflake ParentID; // opt, null
std::string LastPinTimestamp; // opt, can be null even tho docs say otherwise
Snowflake ID; //
ChannelType Type; //
Snowflake GuildID; // opt
int Position = -1; // opt
std::vector<PermissionOverwrite> PermissionOverwrites; // opt
std::string Name; // opt, null (null for dm's)
std::string Topic; // opt, null
bool IsNSFW = false; // opt
Snowflake LastMessageID; // opt, null
int Bitrate = 0; // opt
int UserLimit = 0; // opt
int RateLimitPerUser = 0; // opt
std::vector<User> Recipients; // opt
std::string Icon; // opt, null
Snowflake OwnerID; // opt
Snowflake ApplicationID; // opt
Snowflake ParentID; // opt, null
std::string LastPinTimestamp; // opt, can be null even tho docs say otherwise
friend void from_json(const nlohmann::json &j, Channel &m);
std::optional<PermissionOverwrite> GetOverwrite(Snowflake id) const;
};

View File

@ -194,6 +194,10 @@ const Guild *DiscordClient::GetGuild(Snowflake id) const {
return m_store.GetGuild(id);
}
const GuildMember *DiscordClient::GetMember(Snowflake user_id, Snowflake guild_id) const {
return m_store.GetGuildMemberData(guild_id, user_id);
}
Snowflake DiscordClient::GetMemberHoistedRole(Snowflake guild_id, Snowflake user_id, bool with_color) const {
auto *data = m_store.GetGuildMemberData(guild_id, user_id);
if (data == nullptr) return Snowflake::Invalid;
@ -224,6 +228,91 @@ std::unordered_set<Snowflake> DiscordClient::GetUsersInGuild(Snowflake id) const
return std::unordered_set<Snowflake>();
}
std::unordered_set<Snowflake> DiscordClient::GetRolesInGuild(Snowflake id) const {
std::unordered_set<Snowflake> ret;
const auto &roles = m_store.GetRoles();
for (const auto &[rid, rdata] : roles)
ret.insert(rid);
return ret;
}
bool DiscordClient::HasGuildPermission(Snowflake user_id, Snowflake guild_id, Permission perm) const {
const auto base = ComputePermissions(user_id, guild_id);
return (base & perm) == perm;
}
bool DiscordClient::HasChannelPermission(Snowflake user_id, Snowflake channel_id, Permission perm) const {
const auto *channel = m_store.GetChannel(channel_id);
if (channel == nullptr) return false;
const auto base = ComputePermissions(user_id, channel->GuildID);
const auto overwrites = ComputeOverwrites(base, user_id, channel_id);
return (overwrites & perm) == perm;
}
Permission DiscordClient::ComputePermissions(Snowflake member_id, Snowflake guild_id) const {
const auto *member = GetMember(member_id, guild_id);
const auto *guild = GetGuild(guild_id);
if (member == nullptr || guild == nullptr)
return Permission::NONE;
if (guild->OwnerID == member_id)
return Permission::ALL;
const auto *everyone = GetRole(guild_id);
if (everyone == nullptr)
return Permission::NONE;
Permission perms = everyone->Permissions;
for (const auto role_id : member->Roles) {
const auto *role = GetRole(role_id);
if (role != nullptr)
perms |= role->Permissions;
}
if ((perms & Permission::ADMINISTRATOR) == Permission::ADMINISTRATOR)
return Permission::ALL;
return perms;
}
Permission DiscordClient::ComputeOverwrites(Permission base, Snowflake member_id, Snowflake channel_id) const {
if ((base & Permission::ADMINISTRATOR) == Permission::ADMINISTRATOR)
return Permission::ALL;
const auto *channel = GetChannel(channel_id);
const auto *member = GetMember(member_id, channel->GuildID);
if (member == nullptr || channel == nullptr)
return Permission::NONE;
Permission perms = base;
auto overwrite_everyone = channel->GetOverwrite(channel->GuildID);
if (overwrite_everyone.has_value()) {
perms &= ~overwrite_everyone->Deny;
perms |= overwrite_everyone->Allow;
}
Permission allow = Permission::NONE;
Permission deny = Permission::NONE;
for (const auto role_id : member->Roles) {
const auto overwrite = channel->GetOverwrite(role_id);
if (overwrite.has_value()) {
allow |= overwrite->Allow;
deny |= overwrite->Deny;
}
}
perms &= ~deny;
perms |= allow;
const auto member_overwrite = channel->GetOverwrite(member_id);
if (member_overwrite.has_value()) {
perms &= ~member_overwrite->Deny;
perms |= member_overwrite->Allow;
}
return perms;
}
void DiscordClient::SendChatMessage(std::string content, Snowflake channel) {
// @([^@#]{1,32})#(\\d{4})
CreateMessageObject obj;

View File

@ -77,8 +77,15 @@ public:
const User *GetUser(Snowflake id) const;
const Role *GetRole(Snowflake id) const;
const Guild *GetGuild(Snowflake id) const;
const GuildMember *GetMember(Snowflake user_id, Snowflake guild_id) const;
Snowflake GetMemberHoistedRole(Snowflake guild_id, Snowflake user_id, bool with_color = false) const;
std::unordered_set<Snowflake> GetUsersInGuild(Snowflake id) const;
std::unordered_set<Snowflake> GetRolesInGuild(Snowflake id) const;
bool HasGuildPermission(Snowflake user_id, Snowflake guild_id, Permission perm) const;
bool HasChannelPermission(Snowflake user_id, Snowflake channel_id, Permission perm) const;
Permission ComputePermissions(Snowflake member_id, Snowflake guild_id) const;
Permission ComputeOverwrites(Permission base, Snowflake member_id, Snowflake channel_id) const;
void SendChatMessage(std::string content, Snowflake channel);
void DeleteMessage(Snowflake channel_id, Snowflake id);

View File

@ -12,6 +12,7 @@
#include "usersettings.hpp"
#include "message.hpp"
#include "invite.hpp"
#include "permissions.hpp"
// most stuff below should just be objects that get processed and thrown away immediately

12
discord/permissions.cpp Normal file
View File

@ -0,0 +1,12 @@
#include "permissions.hpp"
void from_json(const nlohmann::json &j, PermissionOverwrite &m) {
JS_D("id", m.ID);
std::string tmp;
JS_D("type", tmp);
m.ID = tmp == "role" ? PermissionOverwrite::ROLE : PermissionOverwrite::MEMBER;
JS_D("allow_new", tmp);
m.Allow = static_cast<Permission>(std::stoull(tmp));
JS_D("deny_new", tmp);
m.Deny = static_cast<Permission>(std::stoull(tmp));
}

60
discord/permissions.hpp Normal file
View File

@ -0,0 +1,60 @@
#pragma once
#include <cstdint>
#include "snowflake.hpp"
#include "json.hpp"
#include "../util.hpp"
enum class Permission {
NONE = 0,
CREATE_INSTANT_INVITE = (1 << 0),
KICK_MEMBERS = (1 << 1),
BAN_MEMBERS = (1 << 2),
ADMINISTRATOR = (1 << 3),
MANAGE_CHANNELS = (1 << 4),
MANAGE_GUILD = (1 << 5),
ADD_REACTIONS = (1 << 6),
VIEW_AUDIT_LOG = (1 << 7),
PRIORITY_SPEAKER = (1 << 8),
STREAM = (1 << 9),
VIEW_CHANNEL = (1 << 10),
SEND_MESSAGES = (1 << 11),
SEND_TTS_MESSAGES = (1 << 12),
MANAGE_MESSAGES = (1 << 13),
EMBED_LINKS = (1 << 14),
ATTACH_FILES = (1 << 15),
READ_MESSAGE_HISTORY = (1 << 16),
MENTION_EVERYONE = (1 << 17),
USE_EXTERNAL_EMOJIS = (1 << 18),
VIEW_GUILD_INSIGHTS = (1 << 19),
CONNECT = (1 << 20),
SPEAK = (1 << 21),
MUTE_MEMBERS = (1 << 22),
DEAFEN_MEMBERS = (1 << 23),
MOVE_MEMBERS = (1 << 24),
USE_VAD = (1 << 25), // voice activity detection
CHANGE_NICKNAME = (1 << 26),
MANAGE_NICKNAMES = (1 << 27),
MANAGE_ROLES = (1 << 28),
MANAGE_WEBHOOKS = (1 << 29),
MANAGE_EMOJIS = (1 << 30),
ALL = 0x7FFFFFFF,
};
template<>
struct Bitwise<Permission> {
static const bool enable = true;
};
struct PermissionOverwrite {
enum OverwriteType {
ROLE,
MEMBER,
};
Snowflake ID;
OverwriteType Type;
Permission Allow;
Permission Deny;
friend void from_json(const nlohmann::json &j, PermissionOverwrite &m);
};

View File

@ -9,7 +9,7 @@ void from_json(const nlohmann::json &j, Role &m) {
JS_D("permissions", m.PermissionsLegacy);
std::string tmp;
JS_D("permissions_new", tmp);
m.Permissions = std::stoull(tmp);
m.Permissions = static_cast<Permission>(std::stoull(tmp));
JS_D("managed", m.IsManaged);
JS_D("mentionable", m.IsMentionable);
}

View File

@ -1,6 +1,7 @@
#pragma once
#include "snowflake.hpp"
#include "json.hpp"
#include "permissions.hpp"
#include <string>
#include <cstdint>
@ -11,7 +12,7 @@ struct Role {
bool IsHoisted;
int Position;
int PermissionsLegacy;
uint64_t Permissions;
Permission Permissions;
bool IsManaged;
bool IsMentionable;

View File

@ -130,6 +130,10 @@ const Store::guilds_type &Store::GetGuilds() const {
return m_guilds;
}
const Store::roles_type &Store::GetRoles() const {
return m_roles;
}
void Store::ClearAll() {
m_channels.clear();
m_guilds.clear();

View File

@ -41,6 +41,7 @@ public:
const channels_type &GetChannels() const;
const guilds_type &GetGuilds() const;
const roles_type &GetRoles() const;
void ClearAll();

View File

@ -9,6 +9,42 @@
#include <string>
#include <iomanip>
template<typename T>
struct Bitwise {
static const bool enable = false;
};
template<typename T>
typename std::enable_if<Bitwise<T>::enable, T>::type operator|(T a, T b) {
using x = typename std::underlying_type<T>::type;
return static_cast<T>(static_cast<x>(a) | static_cast<x>(b));
}
template<typename T>
typename std::enable_if<Bitwise<T>::enable, T>::type operator|=(T &a, T b) {
using x = typename std::underlying_type<T>::type;
a = static_cast<T>(static_cast<x>(a) | static_cast<x>(b));
return a;
}
template<typename T>
typename std::enable_if<Bitwise<T>::enable, T>::type operator&(T a, T b) {
using x = typename std::underlying_type<T>::type;
return static_cast<T>(static_cast<x>(a) & static_cast<x>(b));
}
template<typename T>
typename std::enable_if<Bitwise<T>::enable, T>::type operator&=(T &a, T b) {
using x = typename std::underlying_type<T>::type;
a = static_cast<T>(static_cast<x>(a) & static_cast<x>(b));
return a;
}
template<typename T>
typename std::enable_if<Bitwise<T>::enable, T>::type operator~(T a) {
return static_cast<T>(~static_cast<std::underlying_type<T>::type>(a));
}
template<typename T>
inline void AlphabeticalSort(T start, T end, std::function<std::string(const typename std::iterator_traits<T>::value_type &)> get_string) {
std::sort(start, end, [&](const auto &a, const auto &b) -> bool {