feat(Core/RBAC): Role Based Access Controls (#24641)

Co-authored-by: blinkysc <blinkysc@users.noreply.github.com>
Co-authored-by: Spp <spp@jorge.gr>
Co-authored-by: Spp <spp@tc.plus>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Andrew <47818697+Nyeriah@users.noreply.github.com>
This commit is contained in:
blinkysc
2026-05-01 07:19:30 -05:00
committed by GitHub
parent a783e6c436
commit 779331fc4b
97 changed files with 6276 additions and 1089 deletions
+33 -28
View File
@@ -23,6 +23,7 @@
#include "GameTime.h"
#include "ObjectMgr.h"
#include "Player.h"
#include "RBAC.h"
#include "SocialMgr.h"
#include "World.h"
@@ -218,7 +219,7 @@ void Channel::JoinChannel(Player* player, std::string const& pass)
JoinNotify(player);
playersStore[guid].SetOwnerGM(player->GetSession()->IsGMAccount());
playersStore[guid].SetOwnerGM(player->GetSession()->HasPermission(rbac::RBAC_PERM_CHANGE_CHANNEL_NOT_MODERATOR));
// Custom channel handling
if (!IsConstant())
@@ -296,11 +297,11 @@ void Channel::LeaveChannel(Player* player, bool send)
for (Channel::PlayerContainer::const_iterator itr = playersStore.begin(); itr != playersStore.end(); ++itr)
{
newowner = itr->second.player;
if (itr->second.plrPtr->GetSession()->IsGMAccount())
if (itr->second.plrPtr->GetSession()->HasPermission(rbac::RBAC_PERM_CHANGE_CHANNEL_NOT_MODERATOR))
_isOwnerGM = true;
else
_isOwnerGM = false;
if (!itr->second.plrPtr->GetSession()->GetSecurity())
if (!itr->second.plrPtr->GetSession()->HasPermission(rbac::RBAC_PERM_CHANGE_CHANNEL_NOT_MODERATOR))
break;
}
SetOwner(newowner);
@@ -324,7 +325,7 @@ void Channel::KickOrBan(Player const* player, std::string const& badname, bool b
return;
}
if (!playersStore[good].IsModerator() && !player->GetSession()->IsGMAccount())
if (!playersStore[good].IsModerator() && !player->GetSession()->HasPermission(rbac::RBAC_PERM_CHANGE_CHANNEL_NOT_MODERATOR))
{
WorldPacket data;
MakeNotModerator(&data);
@@ -347,7 +348,7 @@ void Channel::KickOrBan(Player const* player, std::string const& badname, bool b
bool isOnChannel = victim && IsOn(victim);
if (!isOnChannel)
{
if (ban && (player->GetSession()->IsGMAccount() || isGoodConstantModerator))
if (ban && (player->GetSession()->HasPermission(rbac::RBAC_PERM_CHANGE_CHANNEL_NOT_MODERATOR) || isGoodConstantModerator))
{
if (ObjectGuid guid = sCharacterCache->GetCharacterGuidByName(badname))
{
@@ -387,7 +388,7 @@ void Channel::KickOrBan(Player const* player, std::string const& badname, bool b
bool changeowner = _ownerGUID == victim;
bool isBadConstantModerator = _channelRights.moderators.find(badAccId) != _channelRights.moderators.end();
if (!player->GetSession()->IsGMAccount() && !isGoodConstantModerator)
if (!player->GetSession()->HasPermission(rbac::RBAC_PERM_CHANGE_CHANNEL_NOT_MODERATOR) && !isGoodConstantModerator)
{
if (changeowner && good != _ownerGUID)
{
@@ -405,7 +406,7 @@ void Channel::KickOrBan(Player const* player, std::string const& badname, bool b
return;
}
if (isBadConstantModerator || bad->GetSession()->IsGMAccount())
if (isBadConstantModerator || bad->GetSession()->HasPermission(rbac::RBAC_PERM_CHANGE_CHANNEL_NOT_MODERATOR))
{
WorldPacket data;
MakeNotModerator(&data);
@@ -454,7 +455,7 @@ void Channel::KickOrBan(Player const* player, std::string const& badname, bool b
for (Channel::PlayerContainer::const_iterator itr = playersStore.begin(); itr != playersStore.end(); ++itr)
{
newowner = itr->second.player;
if (!itr->second.plrPtr->GetSession()->GetSecurity())
if (!itr->second.plrPtr->GetSession()->HasPermission(rbac::RBAC_PERM_CHANGE_CHANNEL_NOT_MODERATOR))
break;
}
SetOwner(newowner);
@@ -476,7 +477,7 @@ void Channel::UnBan(Player const* player, std::string const& badname)
return;
}
if (!playersStore[good].IsModerator() && !player->GetSession()->IsGMAccount())
if (!playersStore[good].IsModerator() && !player->GetSession()->HasPermission(rbac::RBAC_PERM_CHANGE_CHANNEL_NOT_MODERATOR))
{
WorldPacket data;
MakeNotModerator(&data);
@@ -499,7 +500,7 @@ void Channel::UnBan(Player const* player, std::string const& badname)
}
bool isConstantModerator = _channelRights.moderators.find(player->GetSession()->GetAccountId()) != _channelRights.moderators.end();
if (!player->GetSession()->IsGMAccount() && !isConstantModerator)
if (!player->GetSession()->HasPermission(rbac::RBAC_PERM_CHANGE_CHANNEL_NOT_MODERATOR) && !isConstantModerator)
{
if (_channelRights.flags & CHANNEL_RIGHT_CANT_BAN)
{
@@ -544,7 +545,7 @@ void Channel::Password(Player const* player, std::string const& pass)
return;
}
if (!playersStore[guid].IsModerator() && !player->GetSession()->IsGMAccount())
if (!playersStore[guid].IsModerator() && !player->GetSession()->HasPermission(rbac::RBAC_PERM_CHANGE_CHANNEL_NOT_MODERATOR))
{
WorldPacket data;
MakeNotModerator(&data);
@@ -581,7 +582,7 @@ void Channel::SetMode(Player const* player, std::string const& p2n, bool mod, bo
return;
}
if (!playersStore[guid].IsModerator() && !player->GetSession()->IsGMAccount())
if (!playersStore[guid].IsModerator() && !player->GetSession()->HasPermission(rbac::RBAC_PERM_CHANGE_CHANNEL_NOT_MODERATOR))
{
WorldPacket data;
MakeNotModerator(&data);
@@ -598,7 +599,7 @@ void Channel::SetMode(Player const* player, std::string const& p2n, bool mod, bo
if (!victim || !IsOn(victim) ||
// allow make moderator from another team only if both is GMs
// at this moment this only way to show channel post for GM from another team
((!player->GetSession()->IsGMAccount() || !newp->GetSession()->IsGMAccount()) && player->GetTeamId() != newp->GetTeamId() &&
((!player->GetSession()->HasPermission(rbac::RBAC_PERM_TWO_SIDE_INTERACTION_CHANNEL) || !newp->GetSession()->HasPermission(rbac::RBAC_PERM_TWO_SIDE_INTERACTION_CHANNEL)) && player->GetTeamId() != newp->GetTeamId() &&
!sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHANNEL)))
{
WorldPacket data;
@@ -624,7 +625,7 @@ void Channel::SetMode(Player const* player, std::string const& p2n, bool mod, bo
else
{
bool isGoodConstantModerator = _channelRights.moderators.find(player->GetSession()->GetAccountId()) != _channelRights.moderators.end();
if (!player->GetSession()->IsGMAccount() && !isGoodConstantModerator)
if (!player->GetSession()->HasPermission(rbac::RBAC_PERM_CHANGE_CHANNEL_NOT_MODERATOR) && !isGoodConstantModerator)
{
if (_channelRights.flags & CHANNEL_RIGHT_CANT_MUTE)
{
@@ -652,7 +653,7 @@ void Channel::SetOwner(Player const* player, std::string const& newname)
}
bool isGoodConstantModerator = _channelRights.moderators.find(player->GetSession()->GetAccountId()) != _channelRights.moderators.end();
if (!player->GetSession()->IsGMAccount() && guid != _ownerGUID && !isGoodConstantModerator)
if (!player->GetSession()->HasPermission(rbac::RBAC_PERM_CHANGE_CHANNEL_NOT_MODERATOR) && guid != _ownerGUID && !isGoodConstantModerator)
{
WorldPacket data;
MakeNotOwner(&data);
@@ -706,15 +707,20 @@ void Channel::List(Player const* player)
std::size_t pos = data.wpos();
data << uint32(0); // size of list, placeholder
uint32 gmLevelInWhoList = sWorld->getIntConfig(CONFIG_GM_LEVEL_IN_WHO_LIST);
uint32 count = 0;
if (!(_channelRights.flags & CHANNEL_RIGHT_CANT_SPEAK))
for (PlayerContainer::const_iterator i = playersStore.begin(); i != playersStore.end(); ++i)
if (AccountMgr::IsPlayerAccount(i->second.plrPtr->GetSession()->GetSecurity()))
{
data << i->first;
data << uint8(i->second.flags); // flags seems to be changed...
++count;
}
if (Player* member = i->second.plrPtr)
if ((player->GetSession()->HasPermission(rbac::RBAC_PERM_WHO_SEE_ALL_SEC_LEVELS) ||
member->GetSession()->GetSecurity() <= AccountTypes(gmLevelInWhoList)) &&
member->IsVisibleGloballyFor(player))
{
data << i->first;
data << uint8(i->second.flags); // flags seems to be changed...
++count;
}
data.put<uint32>(pos, count);
@@ -733,7 +739,7 @@ void Channel::Announce(Player const* player)
return;
}
if (!playersStore[guid].IsModerator() && !player->GetSession()->IsGMAccount())
if (!playersStore[guid].IsModerator() && !player->GetSession()->HasPermission(rbac::RBAC_PERM_CHANGE_CHANNEL_NOT_MODERATOR))
{
WorldPacket data;
MakeNotModerator(&data);
@@ -932,7 +938,7 @@ void Channel::SendToAllWatching(WorldPacket* data)
bool Channel::ShouldAnnouncePlayer(Player const* player) const
{
return !(player->GetSession()->IsGMAccount() && sWorld->getBoolConfig(CONFIG_SILENTLY_GM_JOIN_TO_CHANNEL));
return !(player->GetSession()->HasPermission(rbac::RBAC_PERM_SILENTLY_JOIN_CHANNEL) && sWorld->getBoolConfig(CONFIG_SILENTLY_GM_JOIN_TO_CHANNEL));
}
void Channel::Voice(ObjectGuid /*guid1*/, ObjectGuid /*guid2*/)
@@ -1158,7 +1164,7 @@ void Channel::JoinNotify(Player* p)
{
if (_channelRights.flags & CHANNEL_RIGHT_CANT_SPEAK)
return;
if (!AccountMgr::IsPlayerAccount(p->GetSession()->GetSecurity()))
if (p->GetSession()->HasPermission(rbac::RBAC_PERM_SILENTLY_JOIN_CHANNEL))
return;
WorldPacket data(SMSG_USERLIST_ADD, 8 + 1 + 1 + 4 + GetName().size());
@@ -1175,7 +1181,7 @@ void Channel::LeaveNotify(Player* p)
{
if (_channelRights.flags & CHANNEL_RIGHT_CANT_SPEAK)
return;
if (!AccountMgr::IsPlayerAccount(p->GetSession()->GetSecurity()))
if (p->GetSession()->HasPermission(rbac::RBAC_PERM_SILENTLY_JOIN_CHANNEL))
return;
WorldPacket data(SMSG_USERLIST_REMOVE, 8 + 1 + 4 + GetName().size());
@@ -1191,7 +1197,7 @@ void Channel::FlagsNotify(Player* p)
{
if (_channelRights.flags & CHANNEL_RIGHT_CANT_SPEAK)
return;
if (!AccountMgr::IsPlayerAccount(p->GetSession()->GetSecurity()))
if (p->GetSession()->HasPermission(rbac::RBAC_PERM_SILENTLY_JOIN_CHANNEL))
return;
WorldPacket data(SMSG_USERLIST_UPDATE, 8 + 1 + 1 + 4 + GetName().size());
@@ -1230,8 +1236,7 @@ void Channel::ToggleModeration(Player* player)
return;
}
const AccountTypes level = static_cast<AccountTypes>(sWorld->getIntConfig(CONFIG_GM_LEVEL_CHANNEL_MODERATION));
const bool gm = (player->GetSession()->GetSecurity() >= level);
const bool gm = player->GetSession()->HasPermission(rbac::RBAC_PERM_CHANGE_CHANNEL_NOT_MODERATOR);
if (!playersStore[guid].IsModerator() && !gm)
{
+12 -4
View File
@@ -24,6 +24,7 @@
#include "ObjectMgr.h"
#include "Opcodes.h"
#include "Player.h"
#include "RBAC.h"
#include "Realm.h"
#include "ScriptMgr.h"
#include "Tokenize.h"
@@ -54,6 +55,13 @@ bool ChatHandler::IsAvailable(uint32 securityLevel) const
return IsConsole() ? true : m_session->GetSecurity() >= AccountTypes(securityLevel);
}
bool ChatHandler::HasPermission(uint32 permissionId) const
{
if (IsConsole())
return true;
return m_session && m_session->HasPermission(permissionId);
}
bool ChatHandler::HasLowerSecurity(Player* target, ObjectGuid guid, bool strong)
{
WorldSession* target_session = nullptr;
@@ -82,7 +90,7 @@ bool ChatHandler::HasLowerSecurityAccount(WorldSession* target, uint32 target_ac
return false;
// ignore only for non-players for non strong checks (when allow apply command at least to same sec level)
if (!AccountMgr::IsPlayerAccount(m_session->GetSecurity()) && !strong && sWorld->getBoolConfig(CONFIG_GM_LOWER_SECURITY))
if (m_session->HasPermission(rbac::RBAC_PERM_CAN_IGNORE_LOWER_SECURITY_CHECK) && !strong && sWorld->getBoolConfig(CONFIG_GM_LOWER_SECURITY))
return false;
if (target)
@@ -117,7 +125,7 @@ void ChatHandler::SendGMText(std::string_view str)
{
std::vector<std::string_view> lines = Acore::Tokenize(str, '\n', true);
// Session should have permissions to receive global gm messages
if (AccountMgr::IsPlayerAccount(m_session->GetSecurity()))
if (!m_session->HasPermission(rbac::RBAC_PERM_RECEIVE_GLOBAL_GM_TEXTMESSAGE))
return;
for (std::string_view line : lines)
@@ -231,7 +239,7 @@ bool ChatHandler::_ParseCommands(std::string_view text)
return true;
// Pretend commands don't exist for regular players
if (m_session && AccountMgr::IsPlayerAccount(m_session->GetSecurity()) && !sWorld->getBoolConfig(CONFIG_ALLOW_PLAYER_COMMANDS))
if (m_session && !m_session->HasPermission(rbac::RBAC_PERM_COMMANDS_NOTIFY_COMMAND_NOT_FOUND_ERROR) && !sWorld->getBoolConfig(CONFIG_ALLOW_PLAYER_COMMANDS))
return false;
// Send error message for GMs
@@ -358,7 +366,7 @@ std::size_t ChatHandler::BuildChatPacket(WorldPacket& data, ChatMsg chatType, La
if (Player const* playerSender = sender->ToPlayer())
{
chatTag = playerSender->GetChatTag();
gmMessage = playerSender->IsGameMaster();
gmMessage = playerSender->GetSession()->HasPermission(rbac::RBAC_PERM_COMMAND_GM_CHAT);
}
}
+1
View File
@@ -241,6 +241,7 @@ public:
Player* GetPlayer() const;
WorldSession* GetSession() { return m_session; }
bool IsAvailable(uint32 securityLevel) const;
bool HasPermission(uint32 permissionId) const;
protected:
explicit ChatHandler() : m_session(nullptr), sentErrorMessage(false) {} // for CLI subclass
@@ -22,6 +22,7 @@
#include "DatabaseEnv.h"
#include "Log.h"
#include "Player.h"
#include "RBAC.h"
#include "ScriptMgr.h"
#include "StringFormat.h"
#include "Tokenize.h"
@@ -515,6 +516,10 @@ bool Acore::Impl::ChatCommands::ChatCommandNode::IsInvokerVisible(ChatHandler co
if (who.IsConsole() && (_permission.AllowConsole == Acore::ChatCommands::Console::Yes))
return true;
// RBAC permissions start at 200, SEC_* levels are 0-4
if (_permission.RequiredLevel >= rbac::RBAC_PERM_COMMAND_RBAC)
return who.HasPermission(_permission.RequiredLevel);
return !who.IsConsole() && who.IsAvailable(_permission.RequiredLevel);
}