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
+56 -1
View File
@@ -41,6 +41,7 @@
#include "PacketUtilities.h"
#include "Pet.h"
#include "Player.h"
#include "Realm.h"
#include "QueryHolder.h"
#include "ScriptMgr.h"
#include "SocialMgr.h"
@@ -115,6 +116,7 @@ WorldSession::WorldSession(uint32 id, std::string&& name, uint32 accountFlags, s
_security(sec),
_skipQueue(skipQueue),
_accountId(id),
_RBACData(nullptr),
_accountName(std::move(name)),
_accountFlags(accountFlags),
m_expansion(expansion),
@@ -171,6 +173,8 @@ WorldSession::~WorldSession()
m_Socket = nullptr;
}
delete _RBACData;
///- empty incoming packet queue
WorldPacket* packet = nullptr;
while (_recvQueue.next(packet))
@@ -360,7 +364,7 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater)
///- Before we process anything:
/// If necessary, kick the player because the client didn't send anything for too long
/// (or they've been idling in character select)
if (sWorld->getBoolConfig(CONFIG_CLOSE_IDLE_CONNECTIONS) && IsConnectionIdle() && m_Socket)
if (IsConnectionIdle() && !HasPermission(rbac::RBAC_PERM_IGNORE_IDLE_CONNECTION))
m_Socket->CloseSocket();
if (updater.ProcessUnsafe())
@@ -1519,3 +1523,54 @@ void WorldSession::SetPacketLogging(bool state)
if (m_Socket)
m_Socket->SetPacketLogging(state);
}
void WorldSession::LoadPermissions()
{
uint32 id = GetAccountId();
uint8 secLevel = GetSecurity();
LOG_DEBUG("rbac", "WorldSession::LoadPermissions [AccountId: {}, Name: {}, realmId: {}, secLevel: {}]",
id, _accountName, realm.Id.Realm, secLevel);
_RBACData = new rbac::RBACData(id, _accountName, realm.Id.Realm, secLevel);
_RBACData->LoadFromDB();
}
QueryCallback WorldSession::LoadPermissionsAsync()
{
uint32 id = GetAccountId();
uint8 secLevel = GetSecurity();
LOG_DEBUG("rbac", "WorldSession::LoadPermissions [AccountId: {}, Name: {}, realmId: {}, secLevel: {}]",
id, _accountName, realm.Id.Realm, secLevel);
_RBACData = new rbac::RBACData(id, _accountName, realm.Id.Realm, secLevel);
return _RBACData->LoadFromDBAsync();
}
bool WorldSession::HasPermission(uint32 permission)
{
if (!_RBACData)
LoadPermissions();
bool hasPermission = _RBACData->HasPermission(permission);
LOG_DEBUG("rbac", "WorldSession::HasPermission [AccountId: {}, Name: {}, realmId: {}]",
_RBACData->GetId(), _RBACData->GetName(), realm.Id.Realm);
return hasPermission;
}
void WorldSession::InvalidateRBACData()
{
LOG_DEBUG("rbac", "WorldSession::InvalidateRBACData [AccountId: {}, Name: {}, realmId: {}]",
_RBACData->GetId(), _RBACData->GetName(), realm.Id.Realm);
delete _RBACData;
_RBACData = nullptr;
}
void WorldSession::InitRBACDataForTest()
{
delete _RBACData;
_RBACData = new rbac::RBACData(_accountId, _accountName, realm.Id.Realm, _security);
_RBACData->LoadFromDBCallback(nullptr);
}
+11
View File
@@ -440,6 +440,16 @@ public:
AccountTypes GetSecurity() const { return _security; }
bool CanSkipQueue() const { return _skipQueue; }
// RBAC
rbac::RBACData* GetRBACData() const { return _RBACData; }
bool HasPermission(uint32 permissionId);
void LoadPermissions();
QueryCallback LoadPermissionsAsync();
void InvalidateRBACData();
/// For unit testing - initializes RBAC data without database access
void InitRBACDataForTest();
uint32 GetAccountId() const { return _accountId; }
Player* GetPlayer() const { return _player; }
std::string const& GetPlayerName() const;
@@ -1224,6 +1234,7 @@ private:
AccountTypes _security;
bool _skipQueue;
uint32 _accountId;
rbac::RBACData* _RBACData;
std::string _accountName;
uint32 _accountFlags;
uint8 m_expansion;
+3 -2
View File
@@ -17,6 +17,7 @@
#include "Chat.h"
#include "ChatPackets.h"
#include "RBAC.h"
#include "GameTime.h"
#include "Metric.h"
#include "Player.h"
@@ -323,7 +324,7 @@ void WorldSessionMgr::AddSession_(WorldSession* session)
// don't count this session when checking player limit
--Sessions;
if (pLimit > 0 && Sessions >= pLimit && AccountMgr::IsPlayerAccount(session->GetSecurity()) && !session->CanSkipQueue() && !HasRecentlyDisconnected(session))
if (pLimit > 0 && Sessions >= pLimit && !session->HasPermission(rbac::RBAC_PERM_SKIP_QUEUE) && !session->CanSkipQueue() && !HasRecentlyDisconnected(session))
{
AddQueuedPlayer(session);
UpdateMaxSessionCounters();
@@ -390,7 +391,7 @@ void WorldSessionMgr::SendGlobalGMMessage(WorldPacket const* packet, WorldSessio
itr->second->GetPlayer() &&
itr->second->GetPlayer()->IsInWorld() &&
itr->second != self &&
!AccountMgr::IsPlayerAccount(itr->second->GetSecurity()) &&
itr->second->HasPermission(rbac::RBAC_PERM_RECEIVE_GLOBAL_GM_TEXTMESSAGE) &&
(teamId == TEAM_NEUTRAL || itr->second->GetPlayer()->GetTeamId() == teamId))
{
itr->second->SendPacket(packet);
+2 -1
View File
@@ -31,6 +31,7 @@
#include "World.h"
#include "WorldSession.h"
#include "WorldSessionMgr.h"
#include "RBAC.h"
#include "zlib.h"
#include <memory>
@@ -763,7 +764,7 @@ bool WorldSocket::HandlePing(WorldPacket& recvPacket)
{
std::unique_lock<std::mutex> sessionGuard(_worldSessionLock);
if (_worldSession && AccountMgr::IsPlayerAccount(_worldSession->GetSecurity()))
if (_worldSession && !_worldSession->HasPermission(rbac::RBAC_PERM_SKIP_CHECK_OVERSPEED_PING))
{
LOG_ERROR("network", "WorldSocket::HandlePing: {} kicked for over-speed pings (address: {})",
_worldSession->GetPlayerInfo(), GetRemoteIpAddress().to_string());