added moderator support for the remote console. #518

This commit is contained in:
oy 2011-07-05 21:54:10 +02:00
parent 5008b5f8a5
commit 1ae474689d
6 changed files with 174 additions and 41 deletions

View file

@ -14,7 +14,10 @@ public:
{
OUTPUT_LEVEL_STANDARD=0,
OUTPUT_LEVEL_ADDINFO,
OUTPUT_LEVEL_DEBUG
OUTPUT_LEVEL_DEBUG,
ACCESS_LEVEL_ADMIN=0,
ACCESS_LEVEL_MOD,
};
// TODO: rework this interface to reduce the amount of virtual calls
@ -62,6 +65,8 @@ public:
virtual void RegisterPrintCallback(FPrintCallback pfnPrintCallback, void *pUserData) = 0;
virtual void Print(int Level, const char *pFrom, const char *pStr) = 0;
virtual void SetAccessLevel(int AccessLevel) = 0;
};
extern IConsole *CreateConsole(int FlagMask);

View file

@ -1,6 +1,7 @@
/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
/* If you are missing that file, acquire a complete release at teeworlds.com. */
#include <base/math.h>
#include <base/system.h>
#include <engine/config.h>
@ -181,6 +182,7 @@ CServer::CServer() : m_DemoRecorder(&m_SnapshotDelta)
m_MapReload = 0;
m_RconClientID = -1;
m_RconAuthLevel = AUTHED_ADMIN;
Init();
}
@ -280,6 +282,11 @@ void CServer::Kick(int ClientID, const char *pReason)
Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", "you can't kick yourself");
return;
}
else if(m_aClients[ClientID].m_Authed > m_RconAuthLevel)
{
Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", "kick command denied");
return;
}
m_NetServer.Drop(ClientID, pReason);
}
@ -571,7 +578,7 @@ int CServer::NewClientCallback(int ClientID, void *pUser)
pThis->m_aClients[ClientID].m_aName[0] = 0;
pThis->m_aClients[ClientID].m_aClan[0] = 0;
pThis->m_aClients[ClientID].m_Country = -1;
pThis->m_aClients[ClientID].m_Authed = 0;
pThis->m_aClients[ClientID].m_Authed = AUTHED_NO;
pThis->m_aClients[ClientID].m_AuthTries = 0;
pThis->m_aClients[ClientID].Reset();
return 0;
@ -596,7 +603,7 @@ int CServer::DelClientCallback(int ClientID, const char *pReason, void *pUser)
pThis->m_aClients[ClientID].m_aName[0] = 0;
pThis->m_aClients[ClientID].m_aClan[0] = 0;
pThis->m_aClients[ClientID].m_Country = -1;
pThis->m_aClients[ClientID].m_Authed = 0;
pThis->m_aClients[ClientID].m_Authed = AUTHED_NO;
pThis->m_aClients[ClientID].m_AuthTries = 0;
pThis->m_aClients[ClientID].m_Snapshots.PurgeAll();
return 0;
@ -635,7 +642,7 @@ void CServer::SendRconLineAuthed(const char *pLine, void *pUser)
for(i = 0; i < MAX_CLIENTS; i++)
{
if(pThis->m_aClients[i].m_State != CClient::STATE_EMPTY && pThis->m_aClients[i].m_Authed)
if(pThis->m_aClients[i].m_State != CClient::STATE_EMPTY && pThis->m_aClients[i].m_Authed >= pThis->m_RconAuthLevel)
pThis->SendRconLine(i, pLine);
}
@ -813,8 +820,12 @@ void CServer::ProcessClientPacket(CNetChunk *pPacket)
str_format(aBuf, sizeof(aBuf), "ClientID=%d rcon='%s'", ClientID, pCmd);
Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "server", aBuf);
m_RconClientID = ClientID;
m_RconAuthLevel = m_aClients[ClientID].m_Authed;
Console()->SetAccessLevel(m_aClients[ClientID].m_Authed == AUTHED_ADMIN ? IConsole::ACCESS_LEVEL_ADMIN : IConsole::ACCESS_LEVEL_MOD);
Console()->ExecuteLine(pCmd);
Console()->SetAccessLevel(IConsole::ACCESS_LEVEL_ADMIN);
m_RconClientID = -1;
m_RconAuthLevel = AUTHED_ADMIN;
}
}
else if(Msg == NETMSG_RCON_AUTH)
@ -825,9 +836,9 @@ void CServer::ProcessClientPacket(CNetChunk *pPacket)
if(Unpacker.Error() == 0)
{
if(g_Config.m_SvRconPassword[0] == 0)
if(g_Config.m_SvRconPassword[0] == 0 && g_Config.m_SvRconModPassword[0])
{
SendRconLine(ClientID, "No rcon password set on server. Set sv_rcon_password to enable the remote console.");
SendRconLine(ClientID, "No rcon password set on server. Set sv_rcon_password and/or sv_rcon_mod_password to enable the remote console.");
}
else if(str_comp(pPw, g_Config.m_SvRconPassword) == 0)
{
@ -835,10 +846,22 @@ void CServer::ProcessClientPacket(CNetChunk *pPacket)
Msg.AddInt(1);
SendMsgEx(&Msg, MSGFLAG_VITAL, ClientID, true);
m_aClients[ClientID].m_Authed = 1;
SendRconLine(ClientID, "Authentication successful. Remote console access granted.");
m_aClients[ClientID].m_Authed = AUTHED_ADMIN;
SendRconLine(ClientID, "Admin authentication successful. Full remote console access granted.");
char aBuf[256];
str_format(aBuf, sizeof(aBuf), "ClientID=%d authed", ClientID);
str_format(aBuf, sizeof(aBuf), "ClientID=%d authed (admin)", ClientID);
Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf);
}
else if(str_comp(pPw, g_Config.m_SvRconModPassword) == 0)
{
CMsgPacker Msg(NETMSG_RCON_AUTH_STATUS);
Msg.AddInt(1);
SendMsgEx(&Msg, MSGFLAG_VITAL, ClientID, true);
m_aClients[ClientID].m_Authed = AUTHED_MOD;
SendRconLine(ClientID, "Moderator authentication successful. Limited remote console access granted.");
char aBuf[256];
str_format(aBuf, sizeof(aBuf), "ClientID=%d authed (moderator)", ClientID);
Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", aBuf);
}
else if(g_Config.m_SvRconMaxTries)
@ -1308,6 +1331,20 @@ void CServer::ConBan(IConsole::IResult *pResult, void *pUser)
pServer->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", "you can't ban yourself");
return;
}
for(int i = 0; i < MAX_CLIENTS; ++i)
{
if(i == pServer->m_RconClientID)
continue;
AddrCheck = pServer->m_NetServer.ClientAddr(i);
AddrCheck.port = 0;
if(net_addr_comp(&Addr, &AddrCheck) == 0 && pServer->m_aClients[i].m_Authed > pServer->m_RconAuthLevel)
{
pServer->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", "ban command denied");
return;
}
}
}
pServer->BanAdd(Addr, Minutes*60, pReason);
}
@ -1325,6 +1362,11 @@ void CServer::ConBan(IConsole::IResult *pResult, void *pUser)
pServer->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", "you can't ban yourself");
return;
}
else if(pServer->m_aClients[ClientID].m_Authed > pServer->m_RconAuthLevel)
{
pServer->Console()->Print(IConsole::OUTPUT_LEVEL_STANDARD, "server", "ban command denied");
return;
}
Addr = pServer->m_NetServer.ClientAddr(ClientID);
pServer->BanAdd(Addr, Minutes*60, pReason);

View file

@ -49,6 +49,13 @@ public:
class IConsole *Console() { return m_pConsole; }
class IStorage *Storage() { return m_pStorage; }
enum
{
AUTHED_NO=0,
AUTHED_MOD,
AUTHED_ADMIN,
};
class CClient
{
public:
@ -110,6 +117,7 @@ public:
int m_RunServer;
int m_MapReload;
int m_RconClientID;
int m_RconAuthLevel;
int64 m_Lastheartbeat;
//static NETADDR4 master_server;

View file

@ -82,7 +82,8 @@ MACRO_CONFIG_INT(SvMaxClients, sv_max_clients, 8, 1, MAX_CLIENTS, CFGFLAG_SERVER
MACRO_CONFIG_INT(SvMaxClientsPerIP, sv_max_clients_per_ip, 4, 1, MAX_CLIENTS, CFGFLAG_SERVER, "Maximum number of clients with the same IP that can connect to the server")
MACRO_CONFIG_INT(SvHighBandwidth, sv_high_bandwidth, 0, 0, 1, CFGFLAG_SERVER, "Use high bandwidth mode. Doubles the bandwidth required for the server. LAN use only")
MACRO_CONFIG_INT(SvRegister, sv_register, 1, 0, 1, CFGFLAG_SERVER, "Register server with master server for public listing")
MACRO_CONFIG_STR(SvRconPassword, sv_rcon_password, 32, "", CFGFLAG_SERVER, "Remote console password")
MACRO_CONFIG_STR(SvRconPassword, sv_rcon_password, 32, "", CFGFLAG_SERVER, "Remote console password (full access)")
MACRO_CONFIG_STR(SvRconModPassword, sv_rcon_mod_password, 32, "", CFGFLAG_SERVER, "Remote console password for moderators (limited access)")
MACRO_CONFIG_INT(SvRconMaxTries, sv_rcon_max_tries, 3, 0, 100, CFGFLAG_SERVER, "Maximum number of tries for remote console authentication")
MACRO_CONFIG_INT(SvRconBantime, sv_rcon_bantime, 5, 0, 1440, CFGFLAG_SERVER, "The time a client gets banned if remote console authentication fails. 0 makes it just use kick")

View file

@ -1,11 +1,15 @@
/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
/* If you are missing that file, acquire a complete release at teeworlds.com. */
#include <new>
#include <base/math.h>
#include <base/system.h>
#include <engine/shared/protocol.h>
#include <engine/storage.h>
#include "console.h"
#include <engine/shared/protocol.h>
#include "config.h"
#include "console.h"
#include "linereader.h"
const char *CConsole::CResult::GetString(unsigned Index)
@ -247,38 +251,47 @@ void CConsole::ExecuteLineStroked(int Stroke, const char *pStr)
if(ParseStart(&Result, pStr, (pEnd-pStr) + 1) != 0)
return;
if (!*Result.m_pCommand)
if(!*Result.m_pCommand)
return;
CCommand *pCommand = FindCommand(Result.m_pCommand, m_FlagMask);
if(pCommand)
{
int IsStrokeCommand = 0;
if(Result.m_pCommand[0] == '+')
if(pCommand->m_AccessLevel >= m_AccessLevel)
{
// insert the stroke direction token
Result.AddArgument(m_paStrokeStr[Stroke]);
IsStrokeCommand = 1;
}
int IsStrokeCommand = 0;
if(Result.m_pCommand[0] == '+')
{
// insert the stroke direction token
Result.AddArgument(m_paStrokeStr[Stroke]);
IsStrokeCommand = 1;
}
if(Stroke || IsStrokeCommand)
if(Stroke || IsStrokeCommand)
{
if(ParseArgs(&Result, pCommand->m_pParams))
{
char aBuf[256];
str_format(aBuf, sizeof(aBuf), "Invalid arguments... Usage: %s %s", pCommand->m_pName, pCommand->m_pParams);
Print(OUTPUT_LEVEL_STANDARD, "Console", aBuf);
}
else if(m_StoreCommands && pCommand->m_Flags&CFGFLAG_STORE)
{
m_ExecutionQueue.AddEntry();
m_ExecutionQueue.m_pLast->m_pfnCommandCallback = pCommand->m_pfnCallback;
m_ExecutionQueue.m_pLast->m_pCommandUserData = pCommand->m_pUserData;
m_ExecutionQueue.m_pLast->m_Result = Result;
}
else
pCommand->m_pfnCallback(&Result, pCommand->m_pUserData);
}
}
else if(Stroke)
{
if(ParseArgs(&Result, pCommand->m_pParams))
{
char aBuf[256];
str_format(aBuf, sizeof(aBuf), "Invalid arguments... Usage: %s %s", pCommand->m_pName, pCommand->m_pParams);
Print(OUTPUT_LEVEL_STANDARD, "Console", aBuf);
}
else if(m_StoreCommands && pCommand->m_Flags&CFGFLAG_STORE)
{
m_ExecutionQueue.AddEntry();
m_ExecutionQueue.m_pLast->m_pfnCommandCallback = pCommand->m_pfnCallback;
m_ExecutionQueue.m_pLast->m_pCommandUserData = pCommand->m_pUserData;
m_ExecutionQueue.m_pLast->m_Result = Result;
}
else
pCommand->m_pfnCallback(&Result, pCommand->m_pUserData);
char aBuf[256];
str_format(aBuf, sizeof(aBuf), "Access for command %s denied.", Result.m_pCommand);
Print(OUTPUT_LEVEL_STANDARD, "Console", aBuf);
}
}
else if(Stroke)
@ -294,8 +307,7 @@ void CConsole::ExecuteLineStroked(int Stroke, const char *pStr)
void CConsole::PossibleCommands(const char *pStr, int FlagMask, FPossibleCallback pfnCallback, void *pUser)
{
CCommand *pCommand;
for(pCommand = m_pFirstCommand; pCommand; pCommand = pCommand->m_pNext)
for(CCommand *pCommand = m_pFirstCommand; pCommand; pCommand = pCommand->m_pNext)
{
if(pCommand->m_Flags&FlagMask)
{
@ -307,8 +319,7 @@ void CConsole::PossibleCommands(const char *pStr, int FlagMask, FPossibleCallbac
CConsole::CCommand *CConsole::FindCommand(const char *pName, int FlagMask)
{
CCommand *pCommand;
for (pCommand = m_pFirstCommand; pCommand; pCommand = pCommand->m_pNext)
for(CCommand *pCommand = m_pFirstCommand; pCommand; pCommand = pCommand->m_pNext)
{
if(pCommand->m_Flags&FlagMask)
{
@ -383,6 +394,62 @@ void CConsole::Con_Exec(IResult *pResult, void *pUserData)
((CConsole*)pUserData)->ExecuteFile(pResult->GetString(0));
}
void CConsole::ConModCommandAccess(IResult *pResult, void *pUser)
{
CConsole* pConsole = static_cast<CConsole *>(pUser);
char aBuf[128];
CCommand *pCommand = pConsole->FindCommand(pResult->GetString(0), CFGFLAG_SERVER);
if(pCommand)
{
if(pResult->NumArguments() == 2)
{
pCommand->m_AccessLevel = clamp(pResult->GetInteger(1), (int)(ACCESS_LEVEL_ADMIN), (int)(ACCESS_LEVEL_MOD));
str_format(aBuf, sizeof(aBuf), "moderator access for '%s' is now %s", pResult->GetString(0), pCommand->m_AccessLevel ? "enabled" : "disabled");
}
else
str_format(aBuf, sizeof(aBuf), "moderator access for '%s' is %s", pResult->GetString(0), pCommand->m_AccessLevel ? "enabled" : "disabled");
}
else
str_format(aBuf, sizeof(aBuf), "No such command: '%s'.", pResult->GetString(0));
pConsole->Print(OUTPUT_LEVEL_STANDARD, "Console", aBuf);
}
void CConsole::ConModCommandStatus(IResult *pResult, void *pUser)
{
CConsole* pConsole = static_cast<CConsole *>(pUser);
char aBuf[240];
mem_zero(aBuf, sizeof(aBuf));
int Used = 0;
for(CCommand *pCommand = pConsole->m_pFirstCommand; pCommand; pCommand = pCommand->m_pNext)
{
if(pCommand->m_Flags&pConsole->m_FlagMask && pCommand->m_AccessLevel == ACCESS_LEVEL_MOD)
{
int Length = str_length(pCommand->m_pName);
if(Used + Length + 2 < (int)(sizeof(aBuf)))
{
if(Used > 0)
{
Used += 2;
str_append(aBuf, ", ", sizeof(aBuf));
}
str_append(aBuf, pCommand->m_pName, sizeof(aBuf));
Used += Length;
}
else
{
pConsole->Print(OUTPUT_LEVEL_STANDARD, "Console", aBuf);
mem_zero(aBuf, sizeof(aBuf));
str_copy(aBuf, pCommand->m_pName, sizeof(aBuf));
Used = Length;
}
}
}
if(Used > 0)
pConsole->Print(OUTPUT_LEVEL_STANDARD, "Console", aBuf);
}
struct CIntVariableData
{
IConsole *m_pConsole;
@ -463,6 +530,7 @@ static void StrVariableCommand(IConsole::IResult *pResult, void *pUserData)
CConsole::CConsole(int FlagMask)
{
m_FlagMask = FlagMask;
m_AccessLevel = ACCESS_LEVEL_ADMIN;
m_StoreCommands = true;
m_paStrokeStr[0] = "0";
m_paStrokeStr[1] = "1";
@ -478,6 +546,9 @@ CConsole::CConsole(int FlagMask)
Register("echo", "r", CFGFLAG_SERVER|CFGFLAG_CLIENT, Con_Echo, this, "Echo the text");
Register("exec", "r", CFGFLAG_SERVER|CFGFLAG_CLIENT, Con_Exec, this, "Execute the specified file");
Register("mod_command", "s?i", CFGFLAG_SERVER, ConModCommandAccess, this, "Specify command accessibility for moderators");
Register("mod_status", "", CFGFLAG_SERVER, ConModCommandStatus, this, "List all commands which are accessible for moderators");
// TODO: this should disappear
#define MACRO_CONFIG_INT(Name,ScriptName,Def,Min,Max,Flags,Desc) \
{ \
@ -524,14 +595,14 @@ void CConsole::ParseArguments(int NumArgs, const char **ppArguments)
void CConsole::Register(const char *pName, const char *pParams,
int Flags, FCommandCallback pfnFunc, void *pUser, const char *pHelp)
{
CCommand *pCommand = (CCommand *)mem_alloc(sizeof(CCommand), sizeof(void*));
CCommand *pCommand = new(mem_alloc(sizeof(CCommand), sizeof(void*))) CCommand;
pCommand->m_pfnCallback = pfnFunc;
pCommand->m_pUserData = pUser;
pCommand->m_pHelp = pHelp;
pCommand->m_pName = pName;
pCommand->m_pParams = pParams;
pCommand->m_Flags = Flags;
pCommand->m_AccessLevel = ACCESS_LEVEL_ADMIN;
pCommand->m_pNext = m_pFirstCommand;
m_pFirstCommand = pCommand;

View file

@ -13,6 +13,7 @@ class CConsole : public IConsole
public:
CCommand *m_pNext;
int m_Flags;
int m_AccessLevel;
FCommandCallback m_pfnCallback;
void *m_pUserData;
};
@ -41,10 +42,13 @@ class CConsole : public IConsole
CExecFile *m_pFirstExec;
class IStorage *m_pStorage;
int m_AccessLevel;
static void Con_Chain(IResult *pResult, void *pUserData);
static void Con_Echo(IResult *pResult, void *pUserData);
static void Con_Exec(IResult *pResult, void *pUserData);
static void ConModCommandAccess(IResult *pResult, void *pUser);
static void ConModCommandStatus(IConsole::IResult *pResult, void *pUser);
void ExecuteFileRecurse(const char *pFilename);
void ExecuteLineStroked(int Stroke, const char *pStr);
@ -153,6 +157,8 @@ public:
virtual void RegisterPrintCallback(FPrintCallback pfnPrintCallback, void *pUserData);
virtual void Print(int Level, const char *pFrom, const char *pStr);
void SetAccessLevel(int AccessLevel) { m_AccessLevel = clamp(AccessLevel, (int)(ACCESS_LEVEL_ADMIN), (int)(ACCESS_LEVEL_MOD)); }
};
#endif