Verify members in db on expedition invites

Fixes an exploit where multiple accepted cross zone invites could race
with cache updates and allow an expedition to exceed its max members
This commit is contained in:
hg 2021-01-21 19:02:00 -05:00
parent 0f5a7e1317
commit d87ae839a2
3 changed files with 51 additions and 6 deletions

View File

@ -782,7 +782,7 @@ bool Expedition::ProcessAddConflicts(Client* leader_client, Client* add_client,
} }
// swapping ignores the max player count check since it's a 1:1 change // swapping ignores the max player count check since it's a 1:1 change
if (!swapping && GetMemberCount() >= m_max_players) if (!swapping && ExpeditionDatabase::GetMemberCount(m_id) >= m_max_players)
{ {
SendLeaderMessage(leader_client, Chat::Red, DZADD_EXCEED_MAX, { fmt::format_int(m_max_players).str() }); SendLeaderMessage(leader_client, Chat::Red, DZADD_EXCEED_MAX, { fmt::format_int(m_max_players).str() });
has_conflict = true; has_conflict = true;
@ -834,10 +834,14 @@ void Expedition::DzInviteResponse(Client* add_client, bool accepted, const std::
} }
// error if swapping and character was already removed before the accept // error if swapping and character was already removed before the accept
if (was_swap_invite && !HasMember(swap_remove_name)) if (was_swap_invite)
{
auto swap_member = GetMemberData(swap_remove_name);
if (!swap_member.IsValid() || !ExpeditionDatabase::HasMember(m_id, swap_member.char_id))
{ {
has_conflicts = true; has_conflicts = true;
} }
}
if (has_conflicts) if (has_conflicts)
{ {

View File

@ -373,6 +373,48 @@ uint32_t ExpeditionDatabase::GetExpeditionIDFromCharacterID(uint32_t character_i
return expedition_id; return expedition_id;
} }
uint32_t ExpeditionDatabase::GetMemberCount(uint32_t expedition_id)
{
LogExpeditionsDetail("Getting expedition [{}] member count from db", expedition_id);
uint32_t member_count = 0;
if (expedition_id != 0)
{
auto query = fmt::format(SQL(
SELECT COUNT(*)
FROM expedition_members
WHERE expedition_id = {} AND is_current_member = TRUE;
), expedition_id);
auto results = database.QueryDatabase(query);
if (results.Success() && results.RowCount() > 0)
{
auto row = results.begin();
member_count = strtoul(row[0], nullptr, 10);
}
}
return member_count;
}
bool ExpeditionDatabase::HasMember(uint32_t expedition_id, uint32_t character_id)
{
LogExpeditionsDetail("Checking db expedition [{}] for character [{}]", expedition_id, character_id);
if (expedition_id == 0 || character_id == 0)
{
return false;
}
auto query = fmt::format(SQL(
SELECT id
FROM expedition_members
WHERE expedition_id = {} AND character_id = {} AND is_current_member = TRUE;
), expedition_id, character_id);
auto results = database.QueryDatabase(query);
return (results.Success() && results.RowCount() > 0);
}
void ExpeditionDatabase::InsertCharacterLockouts(uint32_t character_id, void ExpeditionDatabase::InsertCharacterLockouts(uint32_t character_id,
const std::vector<ExpeditionLockoutTimer>& lockouts) const std::vector<ExpeditionLockoutTimer>& lockouts)
{ {

View File

@ -58,9 +58,8 @@ namespace ExpeditionDatabase
void DeleteMembersLockout(const std::vector<ExpeditionMember>& members, void DeleteMembersLockout(const std::vector<ExpeditionMember>& members,
const std::string& expedition_name, const std::string& event_name); const std::string& expedition_name, const std::string& event_name);
uint32_t GetExpeditionIDFromCharacterID(uint32_t character_id); uint32_t GetExpeditionIDFromCharacterID(uint32_t character_id);
std::pair<std::vector<ExpeditionLockoutTimer>, std::vector<uint32_t>> GetMembersLockout( uint32_t GetMemberCount(uint32_t expedition_id);
const std::vector<ExpeditionMember>& members, const std::string& expedition_name, bool HasMember(uint32_t expedition_id, uint32_t character_id);
const std::string& event_name);
void InsertCharacterLockouts(uint32_t character_id, void InsertCharacterLockouts(uint32_t character_id,
const std::vector<ExpeditionLockoutTimer>& lockouts); const std::vector<ExpeditionLockoutTimer>& lockouts);
void InsertMembersLockout(const std::vector<ExpeditionMember>& members, void InsertMembersLockout(const std::vector<ExpeditionMember>& members,