diff --git a/core/AdminCache.cpp b/core/AdminCache.cpp index 5f79433b..12dac592 100644 --- a/core/AdminCache.cpp +++ b/core/AdminCache.cpp @@ -35,15 +35,8 @@ AdminCache::AdminCache() AdminCache::~AdminCache() { - if (m_pCmdGrpOverrides) - { - sm_trie_destroy(m_pCmdGrpOverrides); - } - - if (m_pCmdOverrides) - { - sm_trie_destroy(m_pCmdOverrides); - } + sm_trie_destroy(m_pCmdGrpOverrides); + sm_trie_destroy(m_pCmdOverrides); DumpAdminCache(0xFFFFFFFF, false); @@ -193,6 +186,8 @@ AdminId AdminCache::CreateAdmin(const char *name) pUser->magic = USR_MAGIC_SET; pUser->auth.identidx = -1; pUser->auth.index = 0; + pUser->immune_default = false; + pUser->immune_global = false; if (m_FirstUser == INVALID_ADMIN_ID) { @@ -986,9 +981,18 @@ bool AdminCache::AdminInheritGroup(AdminId id, GroupId gid) table[pUser->grp_count] = gid; pUser->grp_count++; - /* Compute new effective flags */ + /* Compute new effective permissions */ pUser->eflags |= pGroup->addflags; + if (pGroup->immune_default) + { + pUser->immune_default = true; + } + if (pGroup->immune_global) + { + pUser->immune_global = true; + } + return true; } @@ -1087,3 +1091,100 @@ unsigned int AdminCache::FlagBitsToArray(FlagBits bits, AdminFlag array[], unsig return num; } + +bool AdminCache::CheckAdminFlags(AdminId id, FlagBits bits) +{ + AdminUser *pUser = (AdminUser *)m_pMemory->GetAddress(id); + if (!pUser || pUser->magic != USR_MAGIC_SET) + { + return false; + } + + return ((pUser->eflags & bits) == bits); +} + +bool AdminCache::CanAdminTarget(AdminId id, AdminId target) +{ + /** + * Zeroth, if the targeting AdminId is INVALID_ADMIN_ID, targeting fails. + * First, if the targetted AdminId is INVALID_ADMIN_ID, targeting succeeds. + */ + + if (id == INVALID_ADMIN_ID) + { + return false; + } + + if (target == INVALID_ADMIN_ID) + { + return true; + } + + AdminUser *pUser = (AdminUser *)m_pMemory->GetAddress(id); + if (!pUser || pUser->magic != USR_MAGIC_SET) + { + return false; + } + + AdminUser *pTarget = (AdminUser *)m_pMemory->GetAddress(target); + if (!pTarget || pTarget->magic != USR_MAGIC_SET) + { + return false; + } + + /** + * Second, if the targeting admin is root, targeting suceeds. + */ + if (pUser->eflags & ADMFLAG_ROOT) + { + return true; + } + + /** Fourth, if the targetted admin has global immunity, targeting fails. */ + if (pTarget->immune_global) + { + return false; + } + + /** + * Fifth, if the targetted admin has default immunity + * and the admin belongs to no groups, targeting fails. + */ + if (pTarget->immune_default && pUser->grp_count < 1) + { + return false; + } + + /** + * Sixth, if the targetted admin has specific immunity from the + * targeting admin via group immunities, targeting fails. + */ + //:TODO: speed this up... maybe with trie hacks. + //idea is to insert %d.%d in the trie after computing this and use it as a cache lookup. + //problem is the trie cannot delete prefixes, so we'd have a problem with invalidations. + if (pTarget->grp_count > 0 && pUser->grp_count > 0) + { + int *grp_table = (int *)m_pMemory->GetAddress(pTarget->grp_table); + int *src_table = (int *)m_pMemory->GetAddress(pUser->grp_table); + GroupId id, other; + unsigned int num; + for (unsigned int i=0; igrp_count; i++) + { + id = grp_table[i]; + num = GetGroupImmunityCount(id); + for (unsigned int j=0; jgrp_count; k++) + { + if (other == src_table[k]) + { + return false; + } + } + } + } + } + + return true; +} diff --git a/core/AdminCache.h b/core/AdminCache.h index c35bf2f2..504be115 100644 --- a/core/AdminCache.h +++ b/core/AdminCache.h @@ -73,6 +73,8 @@ struct AdminUser int next_user; /* Next user in ze list */ int prev_user; /* Prev user in the list */ UserAuth auth; /* Auth method for this user */ + bool immune_global; /* Whether globally immune */ + bool immune_default; /* Whether defaultly immune */ }; class AdminCache : @@ -127,6 +129,8 @@ public: //IAdminSystem FlagBits FlagBitArrayToBits(const bool array[], unsigned int maxSize); FlagBits FlagArrayToBits(const AdminFlag array[], unsigned int numFlags); unsigned int FlagBitsToArray(FlagBits bits, AdminFlag array[], unsigned int maxSize); + bool CheckAdminFlags(AdminId id, FlagBits bits); + bool CanAdminTarget(AdminId id, AdminId target); private: void _UnsetCommandOverride(const char *cmd); void _UnsetCommandGroupOverride(const char *group); diff --git a/public/IAdminSystem.h b/public/IAdminSystem.h index 87558576..db4820d2 100644 --- a/public/IAdminSystem.h +++ b/public/IAdminSystem.h @@ -416,7 +416,7 @@ namespace SourceMod virtual bool GetAdminFlag(AdminId id, AdminFlag flag, AccessMode mode) =0; /** - * @brief Returns a bitarray of flags enabled on an admin. + * @brief Returns the bitstring of access flags on an admin. * * @param id AdminId index of the admin. * @param mode Access mode to use. @@ -524,6 +524,35 @@ namespace SourceMod * @return Number of flags written. */ virtual unsigned int FlagBitsToArray(FlagBits bits, AdminFlag array[], unsigned int maxSize) =0; + + /** + * @brief Checks whether a user has access to a given set of flag bits. + * Note: This is a wrapper around GetAdminFlags(). + * + * @param id AdminId index of admin. + * @param flags Bitstring containing the permissions to check. + * @return True if user has permission, false otherwise. + */ + virtual bool CheckAdminFlags(AdminId id, FlagBits bits) =0; + + /** + * @brief Checks whether an AdminId can target another AdminId. + * + * Zeroth, if the targeting AdminId is INVALID_ADMIN_ID, targeting fails. + * First, if the targetted AdminId is INVALID_ADMIN_ID, targeting succeeds. + * Second, if the targeting admin is root, targeting suceeds. + * Third, if the targetted admin has global immunity, targeting fails. + * Fourth, if the targetted admin has default immunity, + * and the admin belongs to no groups, targeting fails. + * Fifth, if the targetted admin has specific immunity from the + * targeting admin via group immunities, targeting fails. + * Sixth, targeting succeeds if it passes these tests. + * + * @param id AdminId index of admin doing the targeting. Can be INVALID_ADMIN_ID. + * @param target AdminId index of the target admin. Can be INVALID_ADMIN_ID. + * @return True if this admin has permission to target the other admin. + */ + virtual bool CanAdminTarget(AdminId id, AdminId target) =0; }; }